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