• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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( &params );
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 
96 static void
qemulator_fb_update(void * _emulator,int x,int y,int w,int h)97 qemulator_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 
109 static void
qemulator_fb_rotate(void * _emulator,int rotation)110 qemulator_fb_rotate( void*  _emulator, int  rotation )
111 {
112     QEmulator*  emulator = _emulator;
113 
114     qemulator_setup( emulator );
115 }
116 
117 static void
qemulator_fb_poll(void * _emulator)118 qemulator_fb_poll( void* _emulator )
119 {
120     QEmulator* emulator = _emulator;
121     qemulator_refresh(emulator);
122 }
123 
124 QEmulator*
qemulator_get(void)125 qemulator_get(void)
126 {
127     return qemulator;
128 }
129 
130 int
qemulator_init(QEmulator * emulator,AConfig * aconfig,const char * basepath,int x,int y,AndroidOptions * opts)131 qemulator_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 
166 void
qemulator_done(QEmulator * emulator)167 qemulator_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 
188 SkinLayout*
qemulator_get_layout(QEmulator * emulator)189 qemulator_get_layout(QEmulator* emulator)
190 {
191     return emulator->layout;
192 }
193 
194 QFrameBuffer*
qemulator_get_first_framebuffer(QEmulator * emulator)195 qemulator_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 
207 void
qemulator_set_title(QEmulator * emulator)208 qemulator_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 
252 static int
get_device_dpi(AndroidOptions * opts)253 get_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 
268 static double
get_default_scale(AndroidOptions * opts)269 get_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  */
329 static void
handle_key_command(void * opaque,SkinKeyCommand command,int down)330 handle_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 */
qemulator_refresh(QEmulator * emulator)496 static 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 
596 SkinKeyboard*
android_emulator_get_keyboard(void)597 android_emulator_get_keyboard(void)
598 {
599     return qemulator->keyboard;
600 }
601 
602 void
android_emulator_set_window_scale(double scale,int is_dpi)603 android_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 
615 void
android_emulator_set_base_port(int port)616 android_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