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