Home | History | Annotate | Download | only in android
      1 /* Copyright (C) 2011 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 
     13 #include <SDL.h>
     14 #include <SDL_syswm.h>
     15 
     16 #include "android/avd/util.h"
     17 #include "android/globals.h"
     18 #include "android/main-common.h"
     19 #include "android/qemulator.h"
     20 #include "android/display.h"
     21 #include "android/resource.h"
     22 #include "android/skin/image.h"
     23 #include "android/skin/trackball.h"
     24 #include "android/skin/keyboard.h"
     25 #include "android/skin/file.h"
     26 #include "android/skin/window.h"
     27 #include "android/user-config.h"
     28 #include "android/utils/bufprint.h"
     29 #include "android/utils/debug.h"
     30 #include "android/utils/eintr_wrapper.h"
     31 #include "android/utils/path.h"
     32 
     33 #include "ui/console.h"
     34 
     35 #include <stdlib.h>
     36 
     37 #define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
     38 
     39 /***  CONFIGURATION
     40  ***/
     41 
     42 static AUserConfig*  userConfig;
     43 
     44 void
     45 user_config_init( void )
     46 {
     47     userConfig = auserConfig_new( android_avdInfo );
     48 }
     49 
     50 /* only call this function on normal exits, so that ^C doesn't save the configuration */
     51 void
     52 user_config_done( void )
     53 {
     54     int  win_x, win_y;
     55 
     56     if (!userConfig) {
     57         D("no user configuration?");
     58         return;
     59     }
     60 
     61     SDL_WM_GetPos( &win_x, &win_y );
     62     auserConfig_setWindowPos(userConfig, win_x, win_y);
     63     auserConfig_save(userConfig);
     64 }
     65 
     66 void
     67 user_config_get_window_pos( int *window_x, int *window_y )
     68 {
     69     *window_x = *window_y = 10;
     70 
     71     if (userConfig)
     72         auserConfig_getWindowPos(userConfig, window_x, window_y);
     73 }
     74 
     75 /***********************************************************************/
     76 /***********************************************************************/
     77 /*****                                                             *****/
     78 /*****            K E Y S E T   R O U T I N E S                    *****/
     79 /*****                                                             *****/
     80 /***********************************************************************/
     81 /***********************************************************************/
     82 
     83 #define  KEYSET_FILE    "default.keyset"
     84 
     85 SkinKeyset*  android_keyset = NULL;
     86 
     87 static int
     88 load_keyset(const char*  path)
     89 {
     90     if (path_can_read(path)) {
     91         AConfig*  root = aconfig_node("","");
     92         if (!aconfig_load_file(root, path)) {
     93             android_keyset = skin_keyset_new(root);
     94             if (android_keyset != NULL) {
     95                 D( "keyset loaded from: %s", path);
     96                 return 0;
     97             }
     98         }
     99     }
    100     return -1;
    101 }
    102 
    103 void
    104 parse_keyset(const char*  keyset, AndroidOptions*  opts)
    105 {
    106     char   kname[MAX_PATH];
    107     char   temp[MAX_PATH];
    108     char*  p;
    109     char*  end;
    110 
    111     /* append .keyset suffix if needed */
    112     if (strchr(keyset, '.') == NULL) {
    113         p   =  kname;
    114         end = p + sizeof(kname);
    115         p   = bufprint(p, end, "%s.keyset", keyset);
    116         if (p >= end) {
    117             derror( "keyset name too long: '%s'\n", keyset);
    118             exit(1);
    119         }
    120         keyset = kname;
    121     }
    122 
    123     /* look for a the keyset file */
    124     p   = temp;
    125     end = p + sizeof(temp);
    126     p = bufprint_config_file(p, end, keyset);
    127     if (p < end && load_keyset(temp) == 0)
    128         return;
    129 
    130     p = temp;
    131     p = bufprint(p, end, "%s" PATH_SEP "keysets" PATH_SEP "%s", opts->sysdir, keyset);
    132     if (p < end && load_keyset(temp) == 0)
    133         return;
    134 
    135     p = temp;
    136     p = bufprint_app_dir(p, end);
    137     p = bufprint(p, end, PATH_SEP "keysets" PATH_SEP "%s", keyset);
    138     if (p < end && load_keyset(temp) == 0)
    139         return;
    140 
    141     return;
    142 }
    143 
    144 void
    145 write_default_keyset( void )
    146 {
    147     char   path[MAX_PATH];
    148 
    149     bufprint_config_file( path, path+sizeof(path), KEYSET_FILE );
    150 
    151     /* only write if there is no file here */
    152     if (!path_exists(path)) {
    153         int          fd = open( path, O_WRONLY | O_CREAT, 0666 );
    154         const char*  ks = skin_keyset_get_default();
    155 
    156 
    157         D( "writing default keyset file to %s", path );
    158 
    159         if (fd < 0) {
    160             D( "%s: could not create file: %s", __FUNCTION__, strerror(errno) );
    161             return;
    162         }
    163         HANDLE_EINTR(write(fd, ks, strlen(ks)));
    164         IGNORE_EINTR(close(fd));
    165     }
    166 }
    167 
    168 
    169 
    170 /***********************************************************************/
    171 /***********************************************************************/
    172 /*****                                                             *****/
    173 /*****            S D L   S U P P O R T                            *****/
    174 /*****                                                             *****/
    175 /***********************************************************************/
    176 /***********************************************************************/
    177 
    178 void *readpng(const unsigned char*  base, size_t  size, unsigned *_width, unsigned *_height);
    179 
    180 #ifdef CONFIG_DARWIN
    181 #  define  ANDROID_ICON_PNG  "android_icon_256.png"
    182 #else
    183 #  define  ANDROID_ICON_PNG  "android_icon_16.png"
    184 #endif
    185 
    186 static void
    187 sdl_set_window_icon( void )
    188 {
    189     static int  window_icon_set;
    190 
    191     if (!window_icon_set)
    192     {
    193 #ifdef _WIN32
    194         HANDLE         handle = GetModuleHandle( NULL );
    195         HICON          icon   = LoadIcon( handle, MAKEINTRESOURCE(1) );
    196         SDL_SysWMinfo  wminfo;
    197 
    198         SDL_GetWMInfo(&wminfo);
    199 
    200         SetClassLongPtr( wminfo.window, GCLP_HICON, (LONG)icon );
    201 #else  /* !_WIN32 */
    202         unsigned              icon_w, icon_h;
    203         size_t                icon_bytes;
    204         const unsigned char*  icon_data;
    205         void*                 icon_pixels;
    206 
    207         window_icon_set = 1;
    208 
    209         icon_data = android_icon_find( ANDROID_ICON_PNG, &icon_bytes );
    210         if ( !icon_data )
    211             return;
    212 
    213         icon_pixels = readpng( icon_data, icon_bytes, &icon_w, &icon_h );
    214         if ( !icon_pixels )
    215             return;
    216 
    217        /* the data is loaded into memory as RGBA bytes by libpng. we want to manage
    218         * the values as 32-bit ARGB pixels, so swap the bytes accordingly depending
    219         * on our CPU endianess
    220         */
    221         {
    222             unsigned*  d     = icon_pixels;
    223             unsigned*  d_end = d + icon_w*icon_h;
    224 
    225             for ( ; d < d_end; d++ ) {
    226                 unsigned  pix = d[0];
    227 #if HOST_WORDS_BIGENDIAN
    228                 /* R,G,B,A read as RGBA => ARGB */
    229                 pix = ((pix >> 8) & 0xffffff) | (pix << 24);
    230 #else
    231                 /* R,G,B,A read as ABGR => ARGB */
    232                 pix = (pix & 0xff00ff00) | ((pix >> 16) & 0xff) | ((pix & 0xff) << 16);
    233 #endif
    234                 d[0] = pix;
    235             }
    236         }
    237 
    238         SDL_Surface* icon = sdl_surface_from_argb32( icon_pixels, icon_w, icon_h );
    239         if (icon != NULL) {
    240             SDL_WM_SetIcon(icon, NULL);
    241             SDL_FreeSurface(icon);
    242             free( icon_pixels );
    243         }
    244 #endif  /* !_WIN32 */
    245     }
    246 }
    247 
    248 /***********************************************************************/
    249 /***********************************************************************/
    250 /*****                                                             *****/
    251 /*****            S K I N   S U P P O R T                          *****/
    252 /*****                                                             *****/
    253 /***********************************************************************/
    254 /***********************************************************************/
    255 
    256 const char*  skin_network_speed = NULL;
    257 const char*  skin_network_delay = NULL;
    258 
    259 
    260 static void sdl_at_exit(void)
    261 {
    262     user_config_done();
    263     qemulator_done(qemulator_get());
    264     SDL_Quit();
    265 }
    266 
    267 
    268 void sdl_display_init(DisplayState *ds, int full_screen, int  no_frame)
    269 {
    270     QEmulator*    emulator = qemulator_get();
    271     SkinDisplay*  disp     = skin_layout_get_display(emulator->layout);
    272     int           width, height;
    273     char          buf[128];
    274 
    275     if (disp->rotation & 1) {
    276         width  = disp->rect.size.h;
    277         height = disp->rect.size.w;
    278     } else {
    279         width  = disp->rect.size.w;
    280         height = disp->rect.size.h;
    281     }
    282 
    283     snprintf(buf, sizeof buf, "width=%d,height=%d", width, height);
    284     android_display_init(ds, qframebuffer_fifo_get());
    285 }
    286 
    287 typedef struct part_properties part_properties;
    288 struct part_properties {
    289     const char*      name;
    290     int              width;
    291     int              height;
    292     part_properties* next;
    293 };
    294 
    295 part_properties*
    296 read_all_part_properties(AConfig* parts)
    297 {
    298     part_properties* head = NULL;
    299     part_properties* prev = NULL;
    300 
    301     AConfig *node = parts->first_child;
    302     while (node) {
    303         part_properties* t = calloc(1, sizeof(part_properties));
    304         t->name = node->name;
    305 
    306         AConfig* bg = aconfig_find(node, "background");
    307         if (bg != NULL) {
    308             t->width = aconfig_int(bg, "width", 0);
    309             t->height = aconfig_int(bg, "height", 0);
    310         }
    311 
    312         if (prev == NULL) {
    313             head = t;
    314         } else {
    315             prev->next = t;
    316         }
    317         prev = t;
    318         node = node->next;
    319     }
    320 
    321     return head;
    322 }
    323 
    324 void
    325 free_all_part_properties(part_properties* head)
    326 {
    327     part_properties* prev = head;
    328     while (head) {
    329         prev = head;
    330         head = head->next;
    331         free(prev);
    332     }
    333 }
    334 
    335 part_properties*
    336 get_part_properties(part_properties* allparts, char *partname)
    337 {
    338     part_properties* p;
    339     for (p = allparts; p != NULL; p = p->next) {
    340         if (!strcmp(partname, p->name))
    341             return p;
    342     }
    343 
    344     return NULL;
    345 }
    346 
    347 void
    348 add_parts_to_layout(AConfig* layout,
    349                     char* parts[],
    350                     int n_parts,
    351                     part_properties *props,
    352                     int xoffset,
    353                     int x_margin,
    354                     int y_margin)
    355 {
    356     int     i;
    357     int     y = 10;
    358     char    tmp[512];
    359     for (i = 0; i < n_parts; i++) {
    360         part_properties *p = get_part_properties(props, parts[i]);
    361         snprintf(tmp, sizeof tmp,
    362             "part%d {\n \
    363                 name %s\n \
    364                 x %d\n \
    365                 y %d\n \
    366             }",
    367             i + 2,  // layout already has the device part as part1, so start from part2
    368             p->name,
    369             xoffset + x_margin,
    370             y
    371             );
    372         y += p->height + y_margin;
    373         aconfig_load(layout, strdup(tmp));
    374     }
    375 }
    376 
    377 int
    378 load_dynamic_skin(AndroidHwConfig* hwConfig,
    379                   char**           skinDirPath,
    380                   int              width,
    381                   int              height,
    382                   AConfig*         root)
    383 {
    384     char      tmp[1024];
    385     AConfig*  node;
    386     int       i;
    387     int       max_part_width;
    388 
    389     *skinDirPath = avdInfo_getDynamicSkinPath(android_avdInfo);
    390     if (*skinDirPath == NULL) {
    391         dwarning("Unable to locate dynamic skin directory. Will not use dynamic skin.");
    392         return 0;
    393     }
    394 
    395     snprintf(tmp, sizeof(tmp), "%s/layout", *skinDirPath);
    396     D("trying to load skin file '%s'", tmp);
    397 
    398     if(aconfig_load_file(root, tmp) < 0) {
    399         dwarning("could not load skin file '%s', won't use a skin\n", tmp);
    400         return 0;
    401     }
    402 
    403     /* Fix the width and height specified for the "device" part in the layout */
    404     node = aconfig_find(root, "parts");
    405     if (node != NULL) {
    406         node = aconfig_find(node, "device");
    407         if (node != NULL) {
    408             node = aconfig_find(node, "display");
    409             if (node != NULL) {
    410                 snprintf(tmp, sizeof tmp, "%d", width);
    411                 aconfig_set(node, "width", strdup(tmp));
    412                 snprintf(tmp, sizeof tmp, "%d", height);
    413                 aconfig_set(node, "height", strdup(tmp));
    414             }
    415         }
    416     }
    417 
    418     /* The dynamic layout declares all the parts that are available statically
    419        in the layout file. Now we need to dynamically generate the
    420        appropriate layout based on the hardware config */
    421 
    422     part_properties* props = read_all_part_properties(aconfig_find(root, "parts"));
    423 
    424     const int N_PARTS = 4;
    425     char* parts[N_PARTS];
    426     parts[0] = "basic_controls";
    427     parts[1] = hwConfig->hw_mainKeys ? "hwkeys_on" : "hwkeys_off";
    428     parts[2] = hwConfig->hw_dPad ? "dpad_on" : "dpad_off";
    429     parts[3] = hwConfig->hw_keyboard ? "keyboard_on" : "keyboard_off";
    430 
    431     for (i = 0, max_part_width = 0; i < N_PARTS; i++) {
    432         part_properties *p = get_part_properties(props, parts[i]);
    433         if (p != NULL && p->width > max_part_width)
    434                 max_part_width = p->width;
    435     }
    436 
    437     int x_margin = 10;
    438     int y_margin = 10;
    439     snprintf(tmp, sizeof tmp,
    440             "layouts {\n \
    441                 portrait {\n \
    442                     width %d\n \
    443                     height %d\n \
    444                     color 0x404040\n \
    445                     event EV_SW:0:1\n \
    446                     part1 {\n name device\n x 0\n y 0\n}\n \
    447                 }\n \
    448                 landscape {\n \
    449                     width %d\n \
    450                     height %d\n \
    451                     color 0x404040\n \
    452                     event EV_SW:0:0\n \
    453                     dpad-rotation 3\n \
    454                     part1 {\n name device\n x 0\n y %d\n rotation 3\n }\n \
    455                     }\n \
    456                 }\n \
    457              }\n",
    458             width  + max_part_width + 2 * x_margin,
    459             height,
    460             height + max_part_width + 2 * x_margin,
    461             width,
    462             width);
    463     aconfig_load(root, strdup(tmp));
    464 
    465     /* Add parts to portrait orientation */
    466     node = aconfig_find(root, "layouts");
    467     if (node != NULL) {
    468         node = aconfig_find(node, "portrait");
    469         if (node != NULL) {
    470             add_parts_to_layout(node, parts, N_PARTS, props, width, x_margin, y_margin);
    471         }
    472     }
    473 
    474     /* Add parts to landscape orientation */
    475     node = aconfig_find(root, "layouts");
    476     if (node != NULL) {
    477         node = aconfig_find(node, "landscape");
    478         if (node != NULL) {
    479             add_parts_to_layout(node, parts, N_PARTS, props, height, x_margin, y_margin);
    480         }
    481     }
    482 
    483     free_all_part_properties(props);
    484 
    485     return 1;
    486 }
    487 
    488 /* list of skin aliases */
    489 static const struct {
    490     const char*  name;
    491     const char*  alias;
    492 } skin_aliases[] = {
    493     { "QVGA-L", "320x240" },
    494     { "QVGA-P", "240x320" },
    495     { "HVGA-L", "480x320" },
    496     { "HVGA-P", "320x480" },
    497     { "QVGA", "320x240" },
    498     { "HVGA", "320x480" },
    499     { NULL, NULL }
    500 };
    501 
    502 void
    503 parse_skin_files(const char*      skinDirPath,
    504                  const char*      skinName,
    505                  AndroidOptions*  opts,
    506                  AndroidHwConfig* hwConfig,
    507                  AConfig*        *skinConfig,
    508                  char*           *skinPath)
    509 {
    510     char      tmp[1024];
    511     AConfig*  root;
    512     const char* path = NULL;
    513     AConfig*  n;
    514 
    515     root = aconfig_node("", "");
    516 
    517     if (skinName == NULL)
    518         goto DEFAULT_SKIN;
    519 
    520     /* Support skin aliases like QVGA-H QVGA-P, etc...
    521        But first we check if it's a directory that exist before applying
    522        the alias */
    523     int  checkAlias = 1;
    524 
    525     if (skinDirPath != NULL) {
    526         bufprint(tmp, tmp+sizeof(tmp), "%s/%s", skinDirPath, skinName);
    527         if (path_exists(tmp)) {
    528             checkAlias = 0;
    529         } else {
    530             D("there is no '%s' skin in '%s'", skinName, skinDirPath);
    531         }
    532     }
    533 
    534     if (checkAlias) {
    535         int  nn;
    536 
    537         for (nn = 0; ; nn++ ) {
    538             const char*  skin_name  = skin_aliases[nn].name;
    539             const char*  skin_alias = skin_aliases[nn].alias;
    540 
    541             if (!skin_name)
    542                 break;
    543 
    544             if (!strcasecmp( skin_name, skinName )) {
    545                 D("skin name '%s' aliased to '%s'", skinName, skin_alias);
    546                 skinName = skin_alias;
    547                 break;
    548             }
    549         }
    550     }
    551 
    552     /* Magically support skins like "320x240" or "320x240x16" */
    553     if(isdigit(skinName[0])) {
    554         char *x = strchr(skinName, 'x');
    555         if(x && isdigit(x[1])) {
    556             int width = atoi(skinName);
    557             int height = atoi(x+1);
    558             int bpp   = 16;
    559             char* y = strchr(x+1, 'x');
    560             if (y && isdigit(y[1])) {
    561                 bpp = atoi(y+1);
    562             }
    563 
    564             if (opts->dynamic_skin) {
    565                 char *dynamicSkinDirPath;
    566                 if (load_dynamic_skin(hwConfig, &dynamicSkinDirPath, width, height, root)) {
    567                     path = dynamicSkinDirPath;
    568                     D("loaded dynamic skin width=%d height=%d bpp=%d\n", width, height, bpp);
    569                     goto FOUND_SKIN;
    570                 }
    571             }
    572 
    573             snprintf(tmp, sizeof tmp,
    574                     "display {\n  width %d\n  height %d\n bpp %d}\n",
    575                     width, height,bpp);
    576             aconfig_load(root, strdup(tmp));
    577             path = ":";
    578             D("found magic skin width=%d height=%d bpp=%d\n", width, height, bpp);
    579             goto FOUND_SKIN;
    580         }
    581     }
    582 
    583     if (skinDirPath == NULL) {
    584         derror("unknown skin name '%s'", skinName);
    585         exit(1);
    586     }
    587 
    588     snprintf(tmp, sizeof tmp, "%s/%s/layout", skinDirPath, skinName);
    589     D("trying to load skin file '%s'", tmp);
    590 
    591     if(aconfig_load_file(root, tmp) < 0) {
    592         dwarning("could not load skin file '%s', using built-in one\n",
    593                  tmp);
    594         goto DEFAULT_SKIN;
    595     }
    596 
    597     snprintf(tmp, sizeof tmp, "%s/%s/", skinDirPath, skinName);
    598     path = tmp;
    599     goto FOUND_SKIN;
    600 
    601 FOUND_SKIN:
    602     /* the default network speed and latency can now be specified by the device skin */
    603     n = aconfig_find(root, "network");
    604     if (n != NULL) {
    605         skin_network_speed = aconfig_str(n, "speed", 0);
    606         skin_network_delay = aconfig_str(n, "delay", 0);
    607     }
    608 
    609     /* extract framebuffer information from the skin.
    610      *
    611      * for version 1 of the skin format, they are in the top-level
    612      * 'display' element.
    613      *
    614      * for version 2 of the skin format, they are under parts.device.display
    615      */
    616     n = aconfig_find(root, "display");
    617     if (n == NULL) {
    618         n = aconfig_find(root, "parts");
    619         if (n != NULL) {
    620             n = aconfig_find(n, "device");
    621             if (n != NULL) {
    622                 n = aconfig_find(n, "display");
    623             }
    624         }
    625     }
    626 
    627     if (n != NULL) {
    628         int  width  = aconfig_int(n, "width", hwConfig->hw_lcd_width);
    629         int  height = aconfig_int(n, "height", hwConfig->hw_lcd_height);
    630         int  depth  = aconfig_int(n, "bpp", hwConfig->hw_lcd_depth);
    631 
    632         if (width > 0 && height > 0) {
    633             /* The emulated framebuffer wants sizes that are multiples of 4 */
    634             if (((width|height) & 3) != 0) {
    635                 width  = (width+3) & ~3;
    636                 height = (height+3) & ~3;
    637                 D("adjusting LCD dimensions to (%dx%dx)", width, height);
    638             }
    639 
    640             /* only depth values of 16 and 32 are correct. 16 is the default. */
    641             if (depth != 32 && depth != 16) {
    642                 depth = 16;
    643                 D("adjusting LCD bit depth to %d", depth);
    644             }
    645 
    646             hwConfig->hw_lcd_width  = width;
    647             hwConfig->hw_lcd_height = height;
    648             hwConfig->hw_lcd_depth  = depth;
    649         }
    650         else {
    651             D("ignoring invalid skin LCD dimensions (%dx%dx%d)",
    652               width, height, depth);
    653         }
    654     }
    655 
    656     *skinConfig = root;
    657     *skinPath   = strdup(path);
    658     return;
    659 
    660 DEFAULT_SKIN:
    661     {
    662         const unsigned char*  layout_base;
    663         size_t                layout_size;
    664         char*                 base;
    665 
    666         skinName = "<builtin>";
    667 
    668         layout_base = android_resource_find( "layout", &layout_size );
    669         if (layout_base == NULL) {
    670             fprintf(stderr, "Couldn't load builtin skin\n");
    671             exit(1);
    672         }
    673         base = malloc( layout_size+1 );
    674         memcpy( base, layout_base, layout_size );
    675         base[layout_size] = 0;
    676 
    677         D("parsing built-in skin layout file (%d bytes)", (int)layout_size);
    678         aconfig_load(root, base);
    679         path = ":";
    680     }
    681     goto FOUND_SKIN;
    682 }
    683 
    684 
    685 void
    686 init_sdl_ui(AConfig*         skinConfig,
    687             const char*      skinPath,
    688             AndroidOptions*  opts)
    689 {
    690     int  win_x, win_y, flags;
    691 
    692     signal(SIGINT, SIG_DFL);
    693 #ifndef _WIN32
    694     signal(SIGQUIT, SIG_DFL);
    695 #endif
    696 
    697     /* we're not a game, so allow the screensaver to run */
    698     setenv("SDL_VIDEO_ALLOW_SCREENSAVER","1",1);
    699 
    700     flags = SDL_INIT_NOPARACHUTE;
    701     if (!opts->no_window)
    702         flags |= SDL_INIT_VIDEO;
    703 
    704     if(SDL_Init(flags)){
    705         fprintf(stderr, "SDL init failure, reason is: %s\n", SDL_GetError() );
    706         exit(1);
    707     }
    708 
    709     if (!opts->no_window) {
    710         SDL_EnableUNICODE(!opts->raw_keys);
    711         SDL_EnableKeyRepeat(0,0);
    712 
    713         sdl_set_window_icon();
    714     }
    715     else
    716     {
    717 #ifndef _WIN32
    718        /* prevent SIGTTIN and SIGTTOUT from stopping us. this is necessary to be
    719         * able to run the emulator in the background (e.g. "emulator &").
    720         * despite the fact that the emulator should not grab input or try to
    721         * write to the output in normal cases, we're stopped on some systems
    722         * (e.g. OS X)
    723         */
    724         signal(SIGTTIN, SIG_IGN);
    725         signal(SIGTTOU, SIG_IGN);
    726 #endif
    727     }
    728     atexit(sdl_at_exit);
    729 
    730     user_config_get_window_pos(&win_x, &win_y);
    731 
    732     if ( qemulator_init(qemulator_get(), skinConfig, skinPath, win_x, win_y, opts) < 0 ) {
    733         fprintf(stderr, "### Error: could not load emulator skin from '%s'\n", skinPath);
    734         exit(1);
    735     }
    736 
    737     /* add an onion overlay image if needed */
    738     if (opts->onion) {
    739         SkinImage*  onion = skin_image_find_simple( opts->onion );
    740         int         alpha, rotate;
    741 
    742         if ( opts->onion_alpha && 1 == sscanf( opts->onion_alpha, "%d", &alpha ) ) {
    743             alpha = (256*alpha)/100;
    744         } else
    745             alpha = 128;
    746 
    747         if ( opts->onion_rotation && 1 == sscanf( opts->onion_rotation, "%d", &rotate ) ) {
    748             rotate &= 3;
    749         } else
    750             rotate = SKIN_ROTATION_0;
    751 
    752         qemulator_get()->onion          = onion;
    753         qemulator_get()->onion_alpha    = alpha;
    754         qemulator_get()->onion_rotation = rotate;
    755     }
    756 }
    757