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) 22static double get_default_scale( AndroidOptions* opts ); 23 24/* QEmulator structure instance. */ 25static QEmulator qemulator[1]; 26 27static void handle_key_command( void* opaque, SkinKeyCommand command, int param ); 28static void qemulator_refresh(QEmulator* emulator); 29extern void qemu_system_shutdown_request(void); 30 31static void 32qemulator_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 45static void 46qemulator_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 96static void 97qemulator_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 109static void 110qemulator_fb_rotate( void* _emulator, int rotation ) 111{ 112 QEmulator* emulator = _emulator; 113 114 qemulator_setup( emulator ); 115} 116 117static void 118qemulator_fb_poll( void* _emulator ) 119{ 120 QEmulator* emulator = _emulator; 121 qemulator_refresh(emulator); 122} 123 124QEmulator* 125qemulator_get(void) 126{ 127 return qemulator; 128} 129 130int 131qemulator_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 166void 167qemulator_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 188SkinLayout* 189qemulator_get_layout(QEmulator* emulator) 190{ 191 return emulator->layout; 192} 193 194QFrameBuffer* 195qemulator_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 207void 208qemulator_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 252static int 253get_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 268static double 269get_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 */ 329static void 330handle_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 */ 496static 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 596SkinKeyboard* 597android_emulator_get_keyboard(void) 598{ 599 return qemulator->keyboard; 600} 601 602void 603android_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 615void 616android_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