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( ¶ms ); 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