Home | History | Annotate | Download | only in android
      1 /* Copyright (C) 2006-2010 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 "android/utils/debug.h"
     14 #include "android/utils/bufprint.h"
     15 #include "android/globals.h"
     16 #include "android/qemulator.h"
     17 #include "android/protocol/core-commands-api.h"
     18 #include "android/protocol/ui-commands-api.h"
     19 #include "user-events.h"
     20 
     21 #define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
     22 static double get_default_scale( AndroidOptions*  opts );
     23 
     24 /* QEmulator structure instance. */
     25 static QEmulator   qemulator[1];
     26 
     27 static void handle_key_command( void*  opaque, SkinKeyCommand  command, int  param );
     28 static void qemulator_refresh(QEmulator* emulator);
     29 extern void qemu_system_shutdown_request(void);
     30 
     31 static void
     32 qemulator_light_brightness( void* opaque, const char*  light, int  value )
     33 {
     34     QEmulator*  emulator = opaque;
     35 
     36     VERBOSE_PRINT(hw_control,"%s: light='%s' value=%d window=%p", __FUNCTION__, light, value, emulator->window);
     37     if ( !strcmp(light, "lcd_backlight") ) {
     38         emulator->lcd_brightness = value;
     39         if (emulator->window)
     40             skin_window_set_lcd_brightness( emulator->window, value );
     41         return;
     42     }
     43 }
     44 
     45 static void
     46 qemulator_setup( QEmulator*  emulator )
     47 {
     48     AndroidOptions*  opts = emulator->opts;
     49 
     50     if ( !emulator->window && !opts->no_window ) {
     51         SkinLayout*  layout = emulator->layout;
     52         double       scale  = get_default_scale(emulator->opts);
     53 
     54         emulator->window = skin_window_create( layout, emulator->win_x, emulator->win_y, scale, 0);
     55         if (emulator->window == NULL)
     56             return;
     57 
     58         {
     59             SkinTrackBall*           ball;
     60             SkinTrackBallParameters  params;
     61 
     62             params.diameter   = 30;
     63             params.ring       = 2;
     64             params.ball_color = 0xffe0e0e0;
     65             params.dot_color  = 0xff202020;
     66             params.ring_color = 0xff000000;
     67 
     68             ball = skin_trackball_create( &params );
     69             emulator->trackball = ball;
     70             skin_window_set_trackball( emulator->window, ball );
     71 
     72             emulator->lcd_brightness = 128;  /* 50% */
     73             skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
     74         }
     75 
     76         if ( emulator->onion != NULL )
     77             skin_window_set_onion( emulator->window,
     78                                    emulator->onion,
     79                                    emulator->onion_rotation,
     80                                    emulator->onion_alpha );
     81 
     82         qemulator_set_title(emulator);
     83 
     84         skin_window_enable_touch ( emulator->window,
     85                                    !androidHwConfig_isScreenNoTouch(android_hw));
     86         skin_window_enable_dpad  ( emulator->window, android_hw->hw_dPad != 0 );
     87         skin_window_enable_qwerty( emulator->window, android_hw->hw_keyboard != 0 );
     88         skin_window_enable_trackball( emulator->window, android_hw->hw_trackBall != 0 );
     89     }
     90 
     91     /* initialize hardware control support */
     92     uicmd_set_brightness_change_callback(qemulator_light_brightness,
     93                                          emulator);
     94 }
     95 
     96 static void
     97 qemulator_fb_update( void*   _emulator, int  x, int  y, int  w, int  h )
     98 {
     99     QEmulator*  emulator = _emulator;
    100 
    101     if (!emulator->window) {
    102         if (emulator->opts->no_window)
    103             return;
    104         qemulator_setup( emulator );
    105     }
    106     skin_window_update_display( emulator->window, x, y, w, h );
    107 }
    108 
    109 static void
    110 qemulator_fb_rotate( void*  _emulator, int  rotation )
    111 {
    112     QEmulator*  emulator = _emulator;
    113 
    114     qemulator_setup( emulator );
    115 }
    116 
    117 static void
    118 qemulator_fb_poll( void* _emulator )
    119 {
    120     QEmulator* emulator = _emulator;
    121     qemulator_refresh(emulator);
    122 }
    123 
    124 QEmulator*
    125 qemulator_get(void)
    126 {
    127     return qemulator;
    128 }
    129 
    130 int
    131 qemulator_init( QEmulator*       emulator,
    132                 AConfig*         aconfig,
    133                 const char*      basepath,
    134                 int              x,
    135                 int              y,
    136                 AndroidOptions*  opts )
    137 {
    138     emulator->aconfig     = aconfig;
    139     emulator->layout_file = skin_file_create_from_aconfig(aconfig, basepath);
    140     emulator->layout      = emulator->layout_file->layouts;
    141     emulator->keyboard    = skin_keyboard_create(opts->charmap, opts->raw_keys);
    142     emulator->window      = NULL;
    143     emulator->win_x       = x;
    144     emulator->win_y       = y;
    145     emulator->opts[0]     = opts[0];
    146 
    147     /* register as a framebuffer clients for all displays defined in the skin file */
    148     SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
    149         SkinDisplay*  disp = part->display;
    150         if (disp->valid) {
    151             qframebuffer_add_client( disp->qfbuff,
    152                                      emulator,
    153                                      qemulator_fb_update,
    154                                      qemulator_fb_rotate,
    155                                      qemulator_fb_poll,
    156                                      NULL );
    157         }
    158     SKIN_FILE_LOOP_END_PARTS
    159 
    160     skin_keyboard_enable( emulator->keyboard, 1 );
    161     skin_keyboard_on_command( emulator->keyboard, handle_key_command, emulator );
    162 
    163     return 0;
    164 }
    165 
    166 void
    167 qemulator_done(QEmulator* emulator)
    168 {
    169     if (emulator->window) {
    170         skin_window_free(emulator->window);
    171         emulator->window = NULL;
    172     }
    173     if (emulator->trackball) {
    174         skin_trackball_destroy(emulator->trackball);
    175         emulator->trackball = NULL;
    176     }
    177     if (emulator->keyboard) {
    178         skin_keyboard_free(emulator->keyboard);
    179         emulator->keyboard = NULL;
    180     }
    181     emulator->layout = NULL;
    182     if (emulator->layout_file) {
    183         skin_file_free(emulator->layout_file);
    184         emulator->layout_file = NULL;
    185     }
    186 }
    187 
    188 SkinLayout*
    189 qemulator_get_layout(QEmulator* emulator)
    190 {
    191     return emulator->layout;
    192 }
    193 
    194 QFrameBuffer*
    195 qemulator_get_first_framebuffer(QEmulator* emulator)
    196 {
    197     /* register as a framebuffer clients for all displays defined in the skin file */
    198     SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
    199         SkinDisplay*  disp = part->display;
    200         if (disp->valid) {
    201             return disp->qfbuff;
    202         }
    203     SKIN_FILE_LOOP_END_PARTS
    204     return NULL;
    205 }
    206 
    207 void
    208 qemulator_set_title(QEmulator* emulator)
    209 {
    210     char  temp[128], *p=temp, *end=p+sizeof temp;;
    211 
    212     if (emulator->window == NULL)
    213         return;
    214 
    215     if (emulator->show_trackball) {
    216         SkinKeyBinding  bindings[ SKIN_KEY_COMMAND_MAX_BINDINGS ];
    217         int             count;
    218 
    219         count = skin_keyset_get_bindings( android_keyset,
    220                                           SKIN_KEY_COMMAND_TOGGLE_TRACKBALL,
    221                                           bindings );
    222 
    223         if (count > 0) {
    224             int  nn;
    225             p = bufprint( p, end, "Press " );
    226             for (nn = 0; nn < count; nn++) {
    227                 if (nn > 0) {
    228                     if (nn < count-1)
    229                         p = bufprint(p, end, ", ");
    230                     else
    231                         p = bufprint(p, end, " or ");
    232                 }
    233                 p = bufprint(p, end, "%s",
    234                              skin_key_symmod_to_str( bindings[nn].sym,
    235                                                      bindings[nn].mod ) );
    236             }
    237             p = bufprint(p, end, " to leave trackball mode. ");
    238         }
    239     }
    240 
    241     p = bufprint(p, end, "%d:%s",
    242                  android_base_port,
    243                  avdInfo_getName( android_avdInfo ));
    244 
    245     skin_window_set_title( emulator->window, temp );
    246 }
    247 
    248 /*
    249  * Helper routines
    250  */
    251 
    252 static int
    253 get_device_dpi( AndroidOptions*  opts )
    254 {
    255     int    dpi_device  = corecmd_get_hw_lcd_density();
    256 
    257     if (opts->dpi_device != NULL) {
    258         char*  end;
    259         dpi_device = strtol( opts->dpi_device, &end, 0 );
    260         if (end == NULL || *end != 0 || dpi_device <= 0) {
    261             fprintf(stderr, "argument for -dpi-device must be a positive integer. Aborting\n" );
    262             exit(1);
    263         }
    264     }
    265     return  dpi_device;
    266 }
    267 
    268 static double
    269 get_default_scale( AndroidOptions*  opts )
    270 {
    271     int     dpi_device  = get_device_dpi( opts );
    272     int     dpi_monitor = -1;
    273     double  scale       = 0.0;
    274 
    275     /* possible values for the 'scale' option are
    276      *   'auto'        : try to determine the scale automatically
    277      *   '<number>dpi' : indicates the host monitor dpi, compute scale accordingly
    278      *   '<fraction>'  : use direct scale coefficient
    279      */
    280 
    281     if (opts->scale) {
    282         if (!strcmp(opts->scale, "auto"))
    283         {
    284             /* we need to get the host dpi resolution ? */
    285             int   xdpi, ydpi;
    286 
    287             if ( SDL_WM_GetMonitorDPI( &xdpi, &ydpi ) < 0 ) {
    288                 fprintf(stderr, "could not get monitor DPI resolution from system. please use -dpi-monitor to specify one\n" );
    289                 exit(1);
    290             }
    291             D( "system reported monitor resolutions: xdpi=%d ydpi=%d\n", xdpi, ydpi);
    292             dpi_monitor = (xdpi + ydpi+1)/2;
    293         }
    294         else
    295         {
    296             char*   end;
    297             scale = strtod( opts->scale, &end );
    298 
    299             if (end && end[0] == 'd' && end[1] == 'p' && end[2] == 'i' && end[3] == 0) {
    300                 if ( scale < 20 || scale > 1000 ) {
    301                     fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
    302                             "host dpi number must be between 20 and 1000" );
    303                     exit(1);
    304                 }
    305                 dpi_monitor = scale;
    306                 scale       = 0.0;
    307             }
    308             else if (end == NULL || *end != 0) {
    309                 fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
    310                         "not a number or the 'auto' keyword" );
    311                 exit(1);
    312             }
    313             else if ( scale < 0.1 || scale > 3. ) {
    314                 fprintf(stderr, "emulator: ignoring bad -window-scale argument '%s': %s\n", opts->scale,
    315                         "must be between 0.1 and 3.0" );
    316                 exit(1);
    317             }
    318         }
    319     }
    320 
    321     if (scale == 0.0 && dpi_monitor > 0)
    322         scale = dpi_monitor*1.0/dpi_device;
    323 
    324     return scale;
    325 }
    326 
    327 /* used to respond to a given keyboard command shortcut
    328  */
    329 static void
    330 handle_key_command( void*  opaque, SkinKeyCommand  command, int  down )
    331 {
    332     static const struct { SkinKeyCommand  cmd; AndroidKeyCode  kcode; }  keycodes[] =
    333     {
    334         { SKIN_KEY_COMMAND_BUTTON_CALL,        kKeyCodeCall },
    335         { SKIN_KEY_COMMAND_BUTTON_HOME,        kKeyCodeHome },
    336         { SKIN_KEY_COMMAND_BUTTON_BACK,        kKeyCodeBack },
    337         { SKIN_KEY_COMMAND_BUTTON_HANGUP,      kKeyCodeEndCall },
    338         { SKIN_KEY_COMMAND_BUTTON_POWER,       kKeyCodePower },
    339         { SKIN_KEY_COMMAND_BUTTON_SEARCH,      kKeyCodeSearch },
    340         { SKIN_KEY_COMMAND_BUTTON_MENU,        kKeyCodeMenu },
    341         { SKIN_KEY_COMMAND_BUTTON_DPAD_UP,     kKeyCodeDpadUp },
    342         { SKIN_KEY_COMMAND_BUTTON_DPAD_LEFT,   kKeyCodeDpadLeft },
    343         { SKIN_KEY_COMMAND_BUTTON_DPAD_RIGHT,  kKeyCodeDpadRight },
    344         { SKIN_KEY_COMMAND_BUTTON_DPAD_DOWN,   kKeyCodeDpadDown },
    345         { SKIN_KEY_COMMAND_BUTTON_DPAD_CENTER, kKeyCodeDpadCenter },
    346         { SKIN_KEY_COMMAND_BUTTON_VOLUME_UP,   kKeyCodeVolumeUp },
    347         { SKIN_KEY_COMMAND_BUTTON_VOLUME_DOWN, kKeyCodeVolumeDown },
    348         { SKIN_KEY_COMMAND_BUTTON_CAMERA,      kKeyCodeCamera },
    349         { SKIN_KEY_COMMAND_BUTTON_TV,          kKeyCodeTV },
    350         { SKIN_KEY_COMMAND_BUTTON_EPG,         kKeyCodeEPG },
    351         { SKIN_KEY_COMMAND_BUTTON_DVR,         kKeyCodeDVR },
    352         { SKIN_KEY_COMMAND_BUTTON_PREV,        kKeyCodePrevious },
    353         { SKIN_KEY_COMMAND_BUTTON_NEXT,        kKeyCodeNext },
    354         { SKIN_KEY_COMMAND_BUTTON_PLAY,        kKeyCodePlay },
    355         { SKIN_KEY_COMMAND_BUTTON_PAUSE,       kKeyCodePause },
    356         { SKIN_KEY_COMMAND_BUTTON_STOP,        kKeyCodeStop },
    357         { SKIN_KEY_COMMAND_BUTTON_REWIND,      kKeyCodeRewind },
    358         { SKIN_KEY_COMMAND_BUTTON_FFWD,        kKeyCodeFastForward },
    359         { SKIN_KEY_COMMAND_BUTTON_BOOKMARKS,   kKeyCodeBookmarks },
    360         { SKIN_KEY_COMMAND_BUTTON_WINDOW,      kKeyCodeCycleWindows },
    361         { SKIN_KEY_COMMAND_BUTTON_CHANNELUP,   kKeyCodeChannelUp },
    362         { SKIN_KEY_COMMAND_BUTTON_CHANNELDOWN, kKeyCodeChannelDown },
    363         { SKIN_KEY_COMMAND_NONE, 0 }
    364     };
    365     int          nn;
    366 #ifdef CONFIG_TRACE
    367     static int   tracing = 0;
    368 #endif
    369     QEmulator*   emulator = opaque;
    370 
    371 
    372     for (nn = 0; keycodes[nn].kcode != 0; nn++) {
    373         if (command == keycodes[nn].cmd) {
    374             unsigned  code = keycodes[nn].kcode;
    375             if (down)
    376                 code |= 0x200;
    377             user_event_keycode( code );
    378             return;
    379         }
    380     }
    381 
    382     // for the show-trackball command, handle down events to enable, and
    383     // up events to disable
    384     if (command == SKIN_KEY_COMMAND_SHOW_TRACKBALL) {
    385         emulator->show_trackball = (down != 0);
    386         skin_window_show_trackball( emulator->window, emulator->show_trackball );
    387         //qemulator_set_title( emulator );
    388         return;
    389     }
    390 
    391     // only handle down events for the rest
    392     if (down == 0)
    393         return;
    394 
    395     switch (command)
    396     {
    397     case SKIN_KEY_COMMAND_TOGGLE_NETWORK:
    398         {
    399             corecmd_toggle_network();
    400             D( "network is now %s", corecmd_is_network_disabled() ?
    401                                     "disconnected" : "connected" );
    402         }
    403         break;
    404 
    405     case SKIN_KEY_COMMAND_TOGGLE_FULLSCREEN:
    406         if (emulator->window) {
    407             skin_window_toggle_fullscreen(emulator->window);
    408         }
    409         break;
    410 
    411     case SKIN_KEY_COMMAND_TOGGLE_TRACING:
    412         {
    413 #ifdef CONFIG_TRACE
    414             tracing = !tracing;
    415             corecmd_trace_control(tracing);
    416 #endif
    417         }
    418         break;
    419 
    420     case SKIN_KEY_COMMAND_TOGGLE_TRACKBALL:
    421         emulator->show_trackball = !emulator->show_trackball;
    422         skin_window_show_trackball( emulator->window, emulator->show_trackball );
    423         qemulator_set_title(emulator);
    424         break;
    425 
    426     case SKIN_KEY_COMMAND_ONION_ALPHA_UP:
    427     case SKIN_KEY_COMMAND_ONION_ALPHA_DOWN:
    428         if (emulator->onion)
    429         {
    430             int  alpha = emulator->onion_alpha;
    431 
    432             if (command == SKIN_KEY_COMMAND_ONION_ALPHA_UP)
    433                 alpha += 16;
    434             else
    435                 alpha -= 16;
    436 
    437             if (alpha > 256)
    438                 alpha = 256;
    439             else if (alpha < 0)
    440                 alpha = 0;
    441 
    442             emulator->onion_alpha = alpha;
    443             skin_window_set_onion( emulator->window, emulator->onion, emulator->onion_rotation, alpha );
    444             skin_window_redraw( emulator->window, NULL );
    445             //dprint( "onion alpha set to %d (%.f %%)", alpha, alpha/2.56 );
    446         }
    447         break;
    448 
    449     case SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV:
    450     case SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT:
    451         {
    452             SkinLayout*  layout = NULL;
    453 
    454             if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT) {
    455                 layout = emulator->layout->next;
    456                 if (layout == NULL)
    457                     layout = emulator->layout_file->layouts;
    458             }
    459             else if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV) {
    460                 layout = emulator->layout_file->layouts;
    461                 while (layout->next && layout->next != emulator->layout)
    462                     layout = layout->next;
    463             }
    464             if (layout != NULL) {
    465                 SkinRotation  rotation;
    466 
    467                 emulator->layout = layout;
    468                 skin_window_reset( emulator->window, layout );
    469 
    470                 rotation = skin_layout_get_dpad_rotation( layout );
    471 
    472                 if (emulator->keyboard)
    473                     skin_keyboard_set_rotation( emulator->keyboard, rotation );
    474 
    475                 if (emulator->trackball) {
    476                     skin_trackball_set_rotation( emulator->trackball, rotation );
    477                     skin_window_set_trackball( emulator->window, emulator->trackball );
    478                     skin_window_show_trackball( emulator->window, emulator->show_trackball );
    479                 }
    480 
    481                 skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
    482 
    483                 qframebuffer_invalidate_all();
    484                 qframebuffer_check_updates();
    485             }
    486         }
    487         break;
    488 
    489     default:
    490         /* XXX: TODO ? */
    491         ;
    492     }
    493 }
    494 
    495 /* called periodically to poll for user input events */
    496 static void qemulator_refresh(QEmulator* emulator)
    497 {
    498     SDL_Event      ev;
    499     SkinWindow*    window   = emulator->window;
    500     SkinKeyboard*  keyboard = emulator->keyboard;
    501 
    502    /* this will eventually call sdl_update if the content of the VGA framebuffer
    503     * has changed */
    504     qframebuffer_check_updates();
    505 
    506     if (window == NULL)
    507         return;
    508 
    509     while(SDL_PollEvent(&ev)){
    510         switch(ev.type){
    511         case SDL_VIDEOEXPOSE:
    512             skin_window_redraw( window, NULL );
    513             break;
    514 
    515         case SDL_KEYDOWN:
    516 #ifdef _WIN32
    517             /* special code to deal with Alt-F4 properly */
    518             if (ev.key.keysym.sym == SDLK_F4 &&
    519                 ev.key.keysym.mod & KMOD_ALT) {
    520               goto CleanExit;
    521             }
    522 #endif
    523 #ifdef __APPLE__
    524             /* special code to deal with Command-Q properly */
    525             if (ev.key.keysym.sym == SDLK_q &&
    526                 ev.key.keysym.mod & KMOD_META) {
    527               goto CleanExit;
    528             }
    529 #endif
    530             skin_keyboard_process_event( keyboard, &ev, 1 );
    531             break;
    532 
    533         case SDL_KEYUP:
    534             skin_keyboard_process_event( keyboard, &ev, 0 );
    535             break;
    536 
    537         case SDL_MOUSEMOTION:
    538             skin_window_process_event( window, &ev );
    539             break;
    540 
    541         case SDL_MOUSEBUTTONDOWN:
    542         case SDL_MOUSEBUTTONUP:
    543             {
    544                 int  down = (ev.type == SDL_MOUSEBUTTONDOWN);
    545                 if (ev.button.button == 4)
    546                 {
    547                     /* scroll-wheel simulates DPad up */
    548                     AndroidKeyCode  kcode;
    549 
    550                     kcode = // qemulator_rotate_keycode(kKeyCodeDpadUp);
    551                         android_keycode_rotate(kKeyCodeDpadUp,
    552                             skin_layout_get_dpad_rotation(qemulator_get_layout(qemulator_get())));
    553                     user_event_key( kcode, down );
    554                 }
    555                 else if (ev.button.button == 5)
    556                 {
    557                     /* scroll-wheel simulates DPad down */
    558                     AndroidKeyCode  kcode;
    559 
    560                     kcode = // qemulator_rotate_keycode(kKeyCodeDpadDown);
    561                         android_keycode_rotate(kKeyCodeDpadDown,
    562                             skin_layout_get_dpad_rotation(qemulator_get_layout(qemulator_get())));
    563                     user_event_key( kcode, down );
    564                 }
    565                 else if (ev.button.button == SDL_BUTTON_LEFT) {
    566                     skin_window_process_event( window, &ev );
    567                 }
    568 #if 0
    569                 else {
    570                 fprintf(stderr, "... mouse button %s: button=%d state=%04x x=%d y=%d\n",
    571                                 down ? "down" : "up  ",
    572                                 ev.button.button, ev.button.state, ev.button.x, ev.button.y);
    573                 }
    574 #endif
    575             }
    576             break;
    577 
    578         case SDL_QUIT:
    579 #if defined _WIN32 || defined __APPLE__
    580         CleanExit:
    581 #endif
    582             /* only save emulator config through clean exit */
    583             qemulator_done(qemulator_get());
    584             qemu_system_shutdown_request();
    585             return;
    586         }
    587     }
    588 
    589     skin_keyboard_flush( keyboard );
    590 }
    591 
    592 /*
    593  * android/console.c helper routines.
    594  */
    595 
    596 SkinKeyboard*
    597 android_emulator_get_keyboard(void)
    598 {
    599     return qemulator->keyboard;
    600 }
    601 
    602 void
    603 android_emulator_set_window_scale( double  scale, int  is_dpi )
    604 {
    605     QEmulator*  emulator = qemulator;
    606 
    607     if (is_dpi)
    608         scale /= get_device_dpi( emulator->opts );
    609 
    610     if (emulator->window)
    611         skin_window_set_scale( emulator->window, scale );
    612 }
    613 
    614 
    615 void
    616 android_emulator_set_base_port( int  port )
    617 {
    618     /* Base port is already set in the emulator's core. */
    619     qemulator_set_title(qemulator);
    620 }
    621