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