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
qemulator_light_brightness(void * opaque,const char * light,int value)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
qemulator_setup(QEmulator * emulator)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
qemulator_fb_update(void * _emulator,int x,int y,int w,int h)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
qemulator_fb_rotate(void * _emulator,int rotation)111 qemulator_fb_rotate( void* _emulator, int rotation )
112 {
113 QEmulator* emulator = _emulator;
114
115 qemulator_setup( emulator );
116 }
117
118 static void
qemulator_fb_poll(void * _emulator)119 qemulator_fb_poll( void* _emulator )
120 {
121 QEmulator* emulator = _emulator;
122 qemulator_refresh(emulator);
123 }
124
125 QEmulator*
qemulator_get(void)126 qemulator_get(void)
127 {
128 return qemulator;
129 }
130
131 int
qemulator_init(QEmulator * emulator,AConfig * aconfig,const char * basepath,int x,int y,AndroidOptions * opts)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
qemulator_done(QEmulator * emulator)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*
qemulator_get_layout(QEmulator * emulator)190 qemulator_get_layout(QEmulator* emulator)
191 {
192 return emulator->layout;
193 }
194
195 QFrameBuffer*
qemulator_get_first_framebuffer(QEmulator * emulator)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
qemulator_set_title(QEmulator * emulator)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
get_device_dpi(AndroidOptions * opts)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
get_default_scale(AndroidOptions * opts)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
handle_key_command(void * opaque,SkinKeyCommand command,int down)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 */
qemulator_refresh(QEmulator * emulator)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*
android_emulator_get_keyboard(void)592 android_emulator_get_keyboard(void)
593 {
594 return qemulator->keyboard;
595 }
596
597 void
android_emulator_set_window_scale(double scale,int is_dpi)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
android_emulator_set_base_port(int port)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