blob: 3f427b86cecff23dcbd92c89a9b92390a63ad4c3 [file] [log] [blame]
Vladimir Chtchetkine01193622010-05-11 13:07:22 -07001/* 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
Vladimir Chtchetkine01193622010-05-11 13:07:22 -070013#include "android/utils/debug.h"
14#include "android/utils/bufprint.h"
15#include "android/globals.h"
16#include "android/qemulator.h"
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -070017#include "android/ui-core-protocol.h"
David 'Digit' Turner055ae422010-07-27 11:34:16 -070018#include "user-events.h"
Vladimir Chtchetkine01193622010-05-11 13:07:22 -070019
20#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
21static double get_default_scale( AndroidOptions* opts );
22
23/* QEmulator structure instance. */
24static QEmulator qemulator[1];
25
David 'Digit' Turner055ae422010-07-27 11:34:16 -070026static void handle_key_command( void* opaque, SkinKeyCommand command, int param );
27static void qemulator_refresh(QEmulator* emulator);
28
Vladimir Chtchetkine01193622010-05-11 13:07:22 -070029static void
30qemulator_light_brightness( void* opaque, const char* light, int value )
31{
32 QEmulator* emulator = opaque;
33
34 VERBOSE_PRINT(hw_control,"%s: light='%s' value=%d window=%p", __FUNCTION__, light, value, emulator->window);
35 if ( !strcmp(light, "lcd_backlight") ) {
36 emulator->lcd_brightness = value;
37 if (emulator->window)
38 skin_window_set_lcd_brightness( emulator->window, value );
39 return;
40 }
41}
42
43static void
44qemulator_setup( QEmulator* emulator )
45{
46 AndroidOptions* opts = emulator->opts;
47
48 if ( !emulator->window && !opts->no_window ) {
49 SkinLayout* layout = emulator->layout;
50 double scale = get_default_scale(emulator->opts);
51
52 emulator->window = skin_window_create( layout, emulator->win_x, emulator->win_y, scale, 0);
53 if (emulator->window == NULL)
54 return;
55
56 {
57 SkinTrackBall* ball;
58 SkinTrackBallParameters params;
59
60 params.diameter = 30;
61 params.ring = 2;
62 params.ball_color = 0xffe0e0e0;
63 params.dot_color = 0xff202020;
64 params.ring_color = 0xff000000;
65
66 ball = skin_trackball_create( &params );
67 emulator->trackball = ball;
68 skin_window_set_trackball( emulator->window, ball );
69
70 emulator->lcd_brightness = 128; /* 50% */
71 skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
72 }
73
74 if ( emulator->onion != NULL )
75 skin_window_set_onion( emulator->window,
76 emulator->onion,
77 emulator->onion_rotation,
78 emulator->onion_alpha );
79
80 qemulator_set_title(emulator);
81
82 skin_window_enable_touch ( emulator->window, android_hw->hw_touchScreen != 0 );
83 skin_window_enable_dpad ( emulator->window, android_hw->hw_dPad != 0 );
84 skin_window_enable_qwerty( emulator->window, android_hw->hw_keyboard != 0 );
85 skin_window_enable_trackball( emulator->window, android_hw->hw_trackBall != 0 );
86 }
87
88 /* initialize hardware control support */
Vladimir Chtchetkine40575612010-07-08 10:25:06 -070089 android_core_set_brightness_change_callback(qemulator_light_brightness,
90 emulator);
Vladimir Chtchetkine01193622010-05-11 13:07:22 -070091}
92
93static void
94qemulator_fb_update( void* _emulator, int x, int y, int w, int h )
95{
96 QEmulator* emulator = _emulator;
97
98 if (emulator->window)
99 skin_window_update_display( emulator->window, x, y, w, h );
100}
101
102static void
103qemulator_fb_rotate( void* _emulator, int rotation )
104{
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700105 QEmulator* emulator = _emulator;
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700106
107 qemulator_setup( emulator );
108}
109
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700110static void
111qemulator_fb_poll( void* _emulator )
112{
113 QEmulator* emulator = _emulator;
114 qemulator_refresh(emulator);
115}
116
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700117QEmulator*
118qemulator_get(void)
119{
120 return qemulator;
121}
122
123int
124qemulator_init( QEmulator* emulator,
125 AConfig* aconfig,
126 const char* basepath,
127 int x,
128 int y,
129 AndroidOptions* opts )
130{
131 emulator->aconfig = aconfig;
132 emulator->layout_file = skin_file_create_from_aconfig(aconfig, basepath);
133 emulator->layout = emulator->layout_file->layouts;
134 // If we have a custom charmap use it to initialize keyboard.
135 // Otherwise initialize keyboard from configuration settings.
136 // Another way to configure keyboard to use a custom charmap would
137 // be saving a custom charmap name into AConfig's keyboard->charmap
138 // property, and calling single skin_keyboard_create_from_aconfig
139 // routine to initialize keyboard.
140 if (NULL != opts->charmap) {
141 emulator->keyboard = skin_keyboard_create_from_kcm(opts->charmap, opts->raw_keys);
142 } else {
143 emulator->keyboard = skin_keyboard_create_from_aconfig(aconfig, opts->raw_keys);
144 }
145 emulator->window = NULL;
146 emulator->win_x = x;
147 emulator->win_y = y;
148 emulator->opts[0] = opts[0];
149
150 /* register as a framebuffer clients for all displays defined in the skin file */
151 SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
152 SkinDisplay* disp = part->display;
153 if (disp->valid) {
154 qframebuffer_add_client( disp->qfbuff,
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700155 emulator,
156 qemulator_fb_update,
157 qemulator_fb_rotate,
158 qemulator_fb_poll,
159 NULL );
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700160 }
161 SKIN_FILE_LOOP_END_PARTS
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700162
163 skin_keyboard_enable( emulator->keyboard, 1 );
164 skin_keyboard_on_command( emulator->keyboard, handle_key_command, emulator );
165
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700166 return 0;
167}
168
169void
170qemulator_done(QEmulator* emulator)
171{
172 if (emulator->window) {
173 skin_window_free(emulator->window);
174 emulator->window = NULL;
175 }
176 if (emulator->trackball) {
177 skin_trackball_destroy(emulator->trackball);
178 emulator->trackball = NULL;
179 }
180 if (emulator->keyboard) {
181 skin_keyboard_free(emulator->keyboard);
182 emulator->keyboard = NULL;
183 }
184 emulator->layout = NULL;
185 if (emulator->layout_file) {
186 skin_file_free(emulator->layout_file);
187 emulator->layout_file = NULL;
188 }
189}
190
191SkinLayout*
192qemulator_get_layout(QEmulator* emulator)
193{
194 return emulator->layout;
195}
196
Vladimir Chtchetkinecf755ea2011-01-12 14:38:19 -0800197QFrameBuffer*
198qemulator_get_first_framebuffer(QEmulator* emulator)
199{
200 /* register as a framebuffer clients for all displays defined in the skin file */
201 SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
202 SkinDisplay* disp = part->display;
203 if (disp->valid) {
204 return disp->qfbuff;
205 }
206 SKIN_FILE_LOOP_END_PARTS
207 return NULL;
208}
209
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700210void
211qemulator_set_title(QEmulator* emulator)
212{
213 char temp[128], *p=temp, *end=p+sizeof temp;;
214
215 if (emulator->window == NULL)
216 return;
217
218 if (emulator->show_trackball) {
219 SkinKeyBinding bindings[ SKIN_KEY_COMMAND_MAX_BINDINGS ];
220 int count;
221
222 count = skin_keyset_get_bindings( android_keyset,
223 SKIN_KEY_COMMAND_TOGGLE_TRACKBALL,
224 bindings );
225
226 if (count > 0) {
227 int nn;
228 p = bufprint( p, end, "Press " );
229 for (nn = 0; nn < count; nn++) {
230 if (nn > 0) {
231 if (nn < count-1)
232 p = bufprint(p, end, ", ");
233 else
234 p = bufprint(p, end, " or ");
235 }
236 p = bufprint(p, end, "%s",
237 skin_key_symmod_to_str( bindings[nn].sym,
238 bindings[nn].mod ) );
239 }
240 p = bufprint(p, end, " to leave trackball mode. ");
241 }
242 }
243
244 p = bufprint(p, end, "%d:%s",
Vladimir Chtchetkine2fa51732010-07-16 11:19:48 -0700245 android_core_get_base_port(),
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700246 avdInfo_getName( android_avdInfo ));
247
248 skin_window_set_title( emulator->window, temp );
249}
250
251/*
252 * Helper routines
253 */
254
255int
256get_device_dpi( AndroidOptions* opts )
257{
Vladimir Chtchetkinea21ac692010-06-28 10:47:18 -0700258 int dpi_device = android_core_get_hw_lcd_density();
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700259
260 if (opts->dpi_device != NULL) {
261 char* end;
262 dpi_device = strtol( opts->dpi_device, &end, 0 );
263 if (end == NULL || *end != 0 || dpi_device <= 0) {
264 fprintf(stderr, "argument for -dpi-device must be a positive integer. Aborting\n" );
265 exit(1);
266 }
267 }
268 return dpi_device;
269}
270
271static double
272get_default_scale( AndroidOptions* opts )
273{
274 int dpi_device = get_device_dpi( opts );
275 int dpi_monitor = -1;
276 double scale = 0.0;
277
278 /* possible values for the 'scale' option are
279 * 'auto' : try to determine the scale automatically
280 * '<number>dpi' : indicates the host monitor dpi, compute scale accordingly
281 * '<fraction>' : use direct scale coefficient
282 */
283
284 if (opts->scale) {
285 if (!strcmp(opts->scale, "auto"))
286 {
287 /* we need to get the host dpi resolution ? */
288 int xdpi, ydpi;
289
290 if ( SDL_WM_GetMonitorDPI( &xdpi, &ydpi ) < 0 ) {
291 fprintf(stderr, "could not get monitor DPI resolution from system. please use -dpi-monitor to specify one\n" );
292 exit(1);
293 }
294 D( "system reported monitor resolutions: xdpi=%d ydpi=%d\n", xdpi, ydpi);
295 dpi_monitor = (xdpi + ydpi+1)/2;
296 }
297 else
298 {
299 char* end;
300 scale = strtod( opts->scale, &end );
301
302 if (end && end[0] == 'd' && end[1] == 'p' && end[2] == 'i' && end[3] == 0) {
303 if ( scale < 20 || scale > 1000 ) {
304 fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
305 "host dpi number must be between 20 and 1000" );
306 exit(1);
307 }
308 dpi_monitor = scale;
309 scale = 0.0;
310 }
311 else if (end == NULL || *end != 0) {
312 fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
313 "not a number or the 'auto' keyword" );
314 exit(1);
315 }
316 else if ( scale < 0.1 || scale > 3. ) {
317 fprintf(stderr, "emulator: ignoring bad -window-scale argument '%s': %s\n", opts->scale,
318 "must be between 0.1 and 3.0" );
319 exit(1);
320 }
321 }
322 }
323
324 if (scale == 0.0 && dpi_monitor > 0)
325 scale = dpi_monitor*1.0/dpi_device;
326
327 if (scale == 0.0)
328 scale = 1.0;
329
330 return scale;
331}
332
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700333/* used to respond to a given keyboard command shortcut
334 */
335static void
336handle_key_command( void* opaque, SkinKeyCommand command, int down )
337{
338 static const struct { SkinKeyCommand cmd; AndroidKeyCode kcode; } keycodes[] =
339 {
340 { SKIN_KEY_COMMAND_BUTTON_CALL, kKeyCodeCall },
341 { SKIN_KEY_COMMAND_BUTTON_HOME, kKeyCodeHome },
342 { SKIN_KEY_COMMAND_BUTTON_BACK, kKeyCodeBack },
343 { SKIN_KEY_COMMAND_BUTTON_HANGUP, kKeyCodeEndCall },
344 { SKIN_KEY_COMMAND_BUTTON_POWER, kKeyCodePower },
345 { SKIN_KEY_COMMAND_BUTTON_SEARCH, kKeyCodeSearch },
346 { SKIN_KEY_COMMAND_BUTTON_MENU, kKeyCodeMenu },
347 { SKIN_KEY_COMMAND_BUTTON_DPAD_UP, kKeyCodeDpadUp },
348 { SKIN_KEY_COMMAND_BUTTON_DPAD_LEFT, kKeyCodeDpadLeft },
349 { SKIN_KEY_COMMAND_BUTTON_DPAD_RIGHT, kKeyCodeDpadRight },
350 { SKIN_KEY_COMMAND_BUTTON_DPAD_DOWN, kKeyCodeDpadDown },
351 { SKIN_KEY_COMMAND_BUTTON_DPAD_CENTER, kKeyCodeDpadCenter },
352 { SKIN_KEY_COMMAND_BUTTON_VOLUME_UP, kKeyCodeVolumeUp },
353 { SKIN_KEY_COMMAND_BUTTON_VOLUME_DOWN, kKeyCodeVolumeDown },
354 { SKIN_KEY_COMMAND_BUTTON_CAMERA, kKeyCodeCamera },
355 { SKIN_KEY_COMMAND_NONE, 0 }
356 };
357 int nn;
358#ifdef CONFIG_TRACE
359 static int tracing = 0;
360#endif
361 QEmulator* emulator = opaque;
362
363
364 for (nn = 0; keycodes[nn].kcode != 0; nn++) {
365 if (command == keycodes[nn].cmd) {
366 unsigned code = keycodes[nn].kcode;
367 if (down)
368 code |= 0x200;
369 user_event_keycode( code );
370 return;
371 }
372 }
373
374 // for the show-trackball command, handle down events to enable, and
375 // up events to disable
376 if (command == SKIN_KEY_COMMAND_SHOW_TRACKBALL) {
377 emulator->show_trackball = (down != 0);
378 skin_window_show_trackball( emulator->window, emulator->show_trackball );
379 //qemulator_set_title( emulator );
380 return;
381 }
382
383 // only handle down events for the rest
384 if (down == 0)
385 return;
386
387 switch (command)
388 {
389 case SKIN_KEY_COMMAND_TOGGLE_NETWORK:
390 {
Vladimir Chtchetkinecefa7442010-09-01 09:17:11 -0700391 android_core_toggle_network();
392 D( "network is now %s", android_core_is_network_disabled() ?
393 "disconnected" : "connected" );
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700394 }
395 break;
396
397 case SKIN_KEY_COMMAND_TOGGLE_FULLSCREEN:
398 if (emulator->window) {
399 skin_window_toggle_fullscreen(emulator->window);
400 }
401 break;
402
403 case SKIN_KEY_COMMAND_TOGGLE_TRACING:
404 {
405#ifdef CONFIG_TRACE
406 tracing = !tracing;
407 if (tracing)
408 android_core_tracing_start();
409 else
410 android_core_tracing_stop();
411#endif
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 */
491static 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());
Vladimir Chtchetkinecefa7442010-09-01 09:17:11 -0700579 android_core_system_shutdown_request();
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700580 return;
581 }
582 }
583
584 skin_keyboard_flush( keyboard );
585}
586
Vladimir Chtchetkine01193622010-05-11 13:07:22 -0700587/*
588 * android/console.c helper routines.
589 */
590
591SkinKeyboard*
592android_emulator_get_keyboard(void)
593{
594 return qemulator->keyboard;
595}
596
597void
598android_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
David 'Digit' Turner3cf34f22010-07-30 16:53:39 -0700609
610void
611android_emulator_set_base_port( int port )
612{
Vladimir Chtchetkinecefa7442010-09-01 09:17:11 -0700613 /* Base port is already set in the emulator's core. */
David 'Digit' Turner3cf34f22010-07-30 16:53:39 -0700614 qemulator_set_title(qemulator);
615}