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/ui-core-protocol.h"
18
19#define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
20static double get_default_scale( AndroidOptions*  opts );
21
22/* QEmulator structure instance. */
23static QEmulator   qemulator[1];
24
25static void
26qemulator_light_brightness( void* opaque, const char*  light, int  value )
27{
28    QEmulator*  emulator = opaque;
29
30    VERBOSE_PRINT(hw_control,"%s: light='%s' value=%d window=%p", __FUNCTION__, light, value, emulator->window);
31    if ( !strcmp(light, "lcd_backlight") ) {
32        emulator->lcd_brightness = value;
33        if (emulator->window)
34            skin_window_set_lcd_brightness( emulator->window, value );
35        return;
36    }
37}
38
39static void
40qemulator_setup( QEmulator*  emulator )
41{
42    AndroidOptions*  opts = emulator->opts;
43
44    if ( !emulator->window && !opts->no_window ) {
45        SkinLayout*  layout = emulator->layout;
46        double       scale  = get_default_scale(emulator->opts);
47
48        emulator->window = skin_window_create( layout, emulator->win_x, emulator->win_y, scale, 0);
49        if (emulator->window == NULL)
50            return;
51
52        {
53            SkinTrackBall*           ball;
54            SkinTrackBallParameters  params;
55
56            params.diameter   = 30;
57            params.ring       = 2;
58            params.ball_color = 0xffe0e0e0;
59            params.dot_color  = 0xff202020;
60            params.ring_color = 0xff000000;
61
62            ball = skin_trackball_create( &params );
63            emulator->trackball = ball;
64            skin_window_set_trackball( emulator->window, ball );
65
66            emulator->lcd_brightness = 128;  /* 50% */
67            skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
68        }
69
70        if ( emulator->onion != NULL )
71            skin_window_set_onion( emulator->window,
72                                   emulator->onion,
73                                   emulator->onion_rotation,
74                                   emulator->onion_alpha );
75
76        qemulator_set_title(emulator);
77
78        skin_window_enable_touch ( emulator->window, android_hw->hw_touchScreen != 0 );
79        skin_window_enable_dpad  ( emulator->window, android_hw->hw_dPad != 0 );
80        skin_window_enable_qwerty( emulator->window, android_hw->hw_keyboard != 0 );
81        skin_window_enable_trackball( emulator->window, android_hw->hw_trackBall != 0 );
82    }
83
84    /* initialize hardware control support */
85    android_core_set_brightness_change_callback(qemulator_light_brightness,
86                                                emulator);
87}
88
89static void
90qemulator_fb_update( void*   _emulator, int  x, int  y, int  w, int  h )
91{
92    QEmulator*  emulator = _emulator;
93
94    if (emulator->window)
95        skin_window_update_display( emulator->window, x, y, w, h );
96}
97
98static void
99qemulator_fb_rotate( void*  _emulator, int  rotation )
100{
101    QEmulator*     emulator = _emulator;
102
103    qemulator_setup( emulator );
104}
105
106QEmulator*
107qemulator_get(void)
108{
109    return qemulator;
110}
111
112int
113qemulator_init( QEmulator*       emulator,
114                AConfig*         aconfig,
115                const char*      basepath,
116                int              x,
117                int              y,
118                AndroidOptions*  opts )
119{
120    emulator->aconfig     = aconfig;
121    emulator->layout_file = skin_file_create_from_aconfig(aconfig, basepath);
122    emulator->layout      = emulator->layout_file->layouts;
123    // If we have a custom charmap use it to initialize keyboard.
124    // Otherwise initialize keyboard from configuration settings.
125    // Another way to configure keyboard to use a custom charmap would
126    // be saving a custom charmap name into AConfig's keyboard->charmap
127    // property, and calling single skin_keyboard_create_from_aconfig
128    // routine to initialize keyboard.
129    if (NULL != opts->charmap) {
130        emulator->keyboard = skin_keyboard_create_from_kcm(opts->charmap, opts->raw_keys);
131    } else {
132        emulator->keyboard = skin_keyboard_create_from_aconfig(aconfig, opts->raw_keys);
133    }
134    emulator->window      = NULL;
135    emulator->win_x       = x;
136    emulator->win_y       = y;
137    emulator->opts[0]     = opts[0];
138
139    /* register as a framebuffer clients for all displays defined in the skin file */
140    SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
141        SkinDisplay*  disp = part->display;
142        if (disp->valid) {
143            qframebuffer_add_client( disp->qfbuff,
144                                        emulator,
145                                        qemulator_fb_update,
146                                        qemulator_fb_rotate,
147                                        NULL );
148        }
149    SKIN_FILE_LOOP_END_PARTS
150    return 0;
151}
152
153void
154qemulator_done(QEmulator* emulator)
155{
156    if (emulator->window) {
157        skin_window_free(emulator->window);
158        emulator->window = NULL;
159    }
160    if (emulator->trackball) {
161        skin_trackball_destroy(emulator->trackball);
162        emulator->trackball = NULL;
163    }
164    if (emulator->keyboard) {
165        skin_keyboard_free(emulator->keyboard);
166        emulator->keyboard = NULL;
167    }
168    emulator->layout = NULL;
169    if (emulator->layout_file) {
170        skin_file_free(emulator->layout_file);
171        emulator->layout_file = NULL;
172    }
173}
174
175SkinLayout*
176qemulator_get_layout(QEmulator* emulator)
177{
178    return emulator->layout;
179}
180
181void
182qemulator_set_title(QEmulator* emulator)
183{
184    char  temp[128], *p=temp, *end=p+sizeof temp;;
185
186    if (emulator->window == NULL)
187        return;
188
189    if (emulator->show_trackball) {
190        SkinKeyBinding  bindings[ SKIN_KEY_COMMAND_MAX_BINDINGS ];
191        int             count;
192
193        count = skin_keyset_get_bindings( android_keyset,
194                                          SKIN_KEY_COMMAND_TOGGLE_TRACKBALL,
195                                          bindings );
196
197        if (count > 0) {
198            int  nn;
199            p = bufprint( p, end, "Press " );
200            for (nn = 0; nn < count; nn++) {
201                if (nn > 0) {
202                    if (nn < count-1)
203                        p = bufprint(p, end, ", ");
204                    else
205                        p = bufprint(p, end, " or ");
206                }
207                p = bufprint(p, end, "%s",
208                             skin_key_symmod_to_str( bindings[nn].sym,
209                                                     bindings[nn].mod ) );
210            }
211            p = bufprint(p, end, " to leave trackball mode. ");
212        }
213    }
214
215    p = bufprint(p, end, "%d:%s",
216                 android_core_get_base_port(),
217                 avdInfo_getName( android_avdInfo ));
218
219    skin_window_set_title( emulator->window, temp );
220}
221
222/*
223 * Helper routines
224 */
225
226int
227get_device_dpi( AndroidOptions*  opts )
228{
229    int    dpi_device  = android_core_get_hw_lcd_density();
230
231    if (opts->dpi_device != NULL) {
232        char*  end;
233        dpi_device = strtol( opts->dpi_device, &end, 0 );
234        if (end == NULL || *end != 0 || dpi_device <= 0) {
235            fprintf(stderr, "argument for -dpi-device must be a positive integer. Aborting\n" );
236            exit(1);
237        }
238    }
239    return  dpi_device;
240}
241
242static double
243get_default_scale( AndroidOptions*  opts )
244{
245    int     dpi_device  = get_device_dpi( opts );
246    int     dpi_monitor = -1;
247    double  scale       = 0.0;
248
249    /* possible values for the 'scale' option are
250     *   'auto'        : try to determine the scale automatically
251     *   '<number>dpi' : indicates the host monitor dpi, compute scale accordingly
252     *   '<fraction>'  : use direct scale coefficient
253     */
254
255    if (opts->scale) {
256        if (!strcmp(opts->scale, "auto"))
257        {
258            /* we need to get the host dpi resolution ? */
259            int   xdpi, ydpi;
260
261            if ( SDL_WM_GetMonitorDPI( &xdpi, &ydpi ) < 0 ) {
262                fprintf(stderr, "could not get monitor DPI resolution from system. please use -dpi-monitor to specify one\n" );
263                exit(1);
264            }
265            D( "system reported monitor resolutions: xdpi=%d ydpi=%d\n", xdpi, ydpi);
266            dpi_monitor = (xdpi + ydpi+1)/2;
267        }
268        else
269        {
270            char*   end;
271            scale = strtod( opts->scale, &end );
272
273            if (end && end[0] == 'd' && end[1] == 'p' && end[2] == 'i' && end[3] == 0) {
274                if ( scale < 20 || scale > 1000 ) {
275                    fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
276                            "host dpi number must be between 20 and 1000" );
277                    exit(1);
278                }
279                dpi_monitor = scale;
280                scale       = 0.0;
281            }
282            else if (end == NULL || *end != 0) {
283                fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
284                        "not a number or the 'auto' keyword" );
285                exit(1);
286            }
287            else if ( scale < 0.1 || scale > 3. ) {
288                fprintf(stderr, "emulator: ignoring bad -window-scale argument '%s': %s\n", opts->scale,
289                        "must be between 0.1 and 3.0" );
290                exit(1);
291            }
292        }
293    }
294
295    if (scale == 0.0 && dpi_monitor > 0)
296        scale = dpi_monitor*1.0/dpi_device;
297
298    if (scale == 0.0)
299        scale = 1.0;
300
301    return scale;
302}
303
304/*
305 * android/console.c helper routines.
306 */
307
308SkinKeyboard*
309android_emulator_get_keyboard(void)
310{
311    return qemulator->keyboard;
312}
313
314void
315android_emulator_set_window_scale( double  scale, int  is_dpi )
316{
317    QEmulator*  emulator = qemulator;
318
319    if (is_dpi)
320        scale /= get_device_dpi( emulator->opts );
321
322    if (emulator->window)
323        skin_window_set_scale( emulator->window, scale );
324}
325
326