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, 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