window.c revision c27f813900a3c114562efbb8df1065e94766fc48
1/* Copyright (C) 2007-2008 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#include "android/skin/window.h"
13#include "android/skin/image.h"
14#include "android/skin/scaler.h"
15#include "android/charmap.h"
16#include "android/utils/debug.h"
17#include "android/utils/display.h"
18#include <SDL_syswm.h>
19#include "qemu-common.h"
20#include <math.h>
21
22#include "framebuffer.h"
23
24/* when shrinking, we reduce the pixel ratio by this fixed amount */
25#define  SHRINK_SCALE  0.6
26
27/* maximum value of LCD brighness */
28#define  LCD_BRIGHTNESS_MIN      0
29#define  LCD_BRIGHTNESS_DEFAULT  128
30#define  LCD_BRIGHTNESS_MAX      255
31
32typedef struct Background {
33    SkinImage*   image;
34    SkinRect     rect;
35    SkinPos      origin;
36} Background;
37
38static void
39background_done( Background*  back )
40{
41    skin_image_unref( &back->image );
42}
43
44static void
45background_init( Background*  back, SkinBackground*  sback, SkinLocation*  loc, SkinRect*  frame )
46{
47    SkinRect  r;
48
49    back->image = skin_image_rotate( sback->image, loc->rotation );
50    skin_rect_rotate( &r, &sback->rect, loc->rotation );
51    r.pos.x += loc->anchor.x;
52    r.pos.y += loc->anchor.y;
53
54    back->origin = r.pos;
55    skin_rect_intersect( &back->rect, &r, frame );
56}
57
58static void
59background_redraw( Background*  back, SkinRect*  rect, SDL_Surface*  surface )
60{
61    SkinRect  r;
62
63    if (skin_rect_intersect( &r, rect, &back->rect ) )
64    {
65        SDL_Rect  rd, rs;
66
67        rd.x = r.pos.x;
68        rd.y = r.pos.y;
69        rd.w = r.size.w;
70        rd.h = r.size.h;
71
72        rs.x = r.pos.x - back->origin.x;
73        rs.y = r.pos.y - back->origin.y;
74        rs.w = r.size.w;
75        rs.h = r.size.h;
76
77        SDL_BlitSurface( skin_image_surface(back->image), &rs, surface, &rd );
78        //SDL_UpdateRects( surface, 1, &rd );
79    }
80}
81
82
83typedef struct ADisplay {
84    SkinRect       rect;
85    SkinPos        origin;
86    SkinRotation   rotation;
87    SkinSize       datasize;  /* framebuffer size */
88    void*          data;      /* framebuffer pixels */
89    QFrameBuffer*  qfbuff;
90    SkinImage*     onion;       /* onion image */
91    SkinRect       onion_rect;  /* onion rect, if any */
92    int            brightness;
93} ADisplay;
94
95static void
96display_done( ADisplay*  disp )
97{
98    disp->data   = NULL;
99    disp->qfbuff = NULL;
100    skin_image_unref( &disp->onion );
101}
102
103static int
104display_init( ADisplay*  disp, SkinDisplay*  sdisp, SkinLocation*  loc, SkinRect*  frame )
105{
106    skin_rect_rotate( &disp->rect, &sdisp->rect, loc->rotation );
107    disp->rect.pos.x += loc->anchor.x;
108    disp->rect.pos.y += loc->anchor.y;
109
110    disp->rotation = (loc->rotation + sdisp->rotation) & 3;
111    switch (disp->rotation) {
112        case SKIN_ROTATION_0:
113            disp->origin = disp->rect.pos;
114            break;
115
116        case SKIN_ROTATION_90:
117            disp->origin.x = disp->rect.pos.x + disp->rect.size.w;
118            disp->origin.y = disp->rect.pos.y;
119            break;
120
121        case SKIN_ROTATION_180:
122            disp->origin.x = disp->rect.pos.x + disp->rect.size.w;
123            disp->origin.y = disp->rect.pos.y + disp->rect.size.h;
124            break;
125
126        default:
127            disp->origin.x = disp->rect.pos.x;
128            disp->origin.y = disp->rect.pos.y + disp->rect.size.h;
129            break;
130    }
131    skin_size_rotate( &disp->datasize, &sdisp->rect.size, sdisp->rotation );
132    skin_rect_intersect( &disp->rect, &disp->rect, frame );
133#if 0
134    fprintf(stderr, "... display_init  rect.pos(%d,%d) rect.size(%d,%d) datasize(%d,%d)\n",
135                    disp->rect.pos.x, disp->rect.pos.y,
136                    disp->rect.size.w, disp->rect.size.h,
137                    disp->datasize.w, disp->datasize.h);
138#endif
139    disp->qfbuff = sdisp->qfbuff;
140    disp->data   = sdisp->qfbuff->pixels;
141    disp->onion  = NULL;
142
143    disp->brightness = LCD_BRIGHTNESS_DEFAULT;
144
145    return (disp->data == NULL) ? -1 : 0;
146}
147
148static __inline__ uint32_t  rgb565_to_argb32( uint32_t  pix )
149{
150    uint32_t  r = ((pix & 0xf800) << 8) | ((pix & 0xe000) << 3);
151    uint32_t  g = ((pix & 0x07e0) << 5) | ((pix & 0x0600) >> 1);
152    uint32_t  b = ((pix & 0x001f) << 3) | ((pix & 0x001c) >> 2);
153
154    return 0xff000000 | r | g | b;
155}
156
157
158static void
159display_set_onion( ADisplay*  disp, SkinImage*  onion, SkinRotation  rotation, int  blend )
160{
161    int        onion_w, onion_h;
162    SkinRect*  rect  = &disp->rect;
163    SkinRect*  orect = &disp->onion_rect;
164
165    rotation = (rotation + disp->rotation) & 3;
166
167    skin_image_unref( &disp->onion );
168    disp->onion = skin_image_clone_full( onion, rotation, blend );
169
170    onion_w = skin_image_w(disp->onion);
171    onion_h = skin_image_h(disp->onion);
172
173    switch (rotation) {
174        case SKIN_ROTATION_0:
175            orect->pos = rect->pos;
176            break;
177
178        case SKIN_ROTATION_90:
179            orect->pos.x = rect->pos.x + rect->size.w - onion_w;
180            orect->pos.y = rect->pos.y;
181            break;
182
183        case SKIN_ROTATION_180:
184            orect->pos.x = rect->pos.x + rect->size.w - onion_w;
185            orect->pos.y = rect->pos.y + rect->size.h - onion_h;
186            break;
187
188        default:
189            orect->pos.x = rect->pos.x;
190            orect->pos.y = rect->pos.y + rect->size.h - onion_h;
191    }
192    orect->size.w = onion_w;
193    orect->size.h = onion_h;
194}
195
196#define  DOT_MATRIX  0
197
198#if DOT_MATRIX
199
200static void
201dotmatrix_dither_argb32( unsigned char*  pixels, int  x, int  y, int  w, int  h, int  pitch )
202{
203    static const unsigned dotmatrix_argb32[16] = {
204        0x003f00, 0x00003f, 0x3f0000, 0x000000,
205        0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000,
206        0x3f0000, 0x000000, 0x003f00, 0x00003f,
207        0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000
208    };
209
210    int   yy = y & 3;
211
212    pixels += 4*x + y*pitch;
213
214    for ( ; h > 0; h-- ) {
215        unsigned*  line = (unsigned*) pixels;
216        int        nn, xx = x & 3;
217
218        for (nn = 0; nn < w; nn++) {
219            unsigned  c = line[nn];
220
221            c = c - ((c >> 2) & dotmatrix_argb32[(yy << 2)|xx]);
222
223            xx = (xx + 1) & 3;
224            line[nn] = c;
225        }
226
227        yy      = (yy + 1) & 3;
228        pixels += pitch;
229    }
230}
231
232#endif /* DOT_MATRIX */
233
234/* technical note about the lightness emulation
235 *
236 * we try to emulate something that looks like the Dream's
237 * non-linear LCD lightness, without going too dark or bright.
238 *
239 * the default lightness is around 105 (about 40%) and we prefer
240 * to keep full RGB colors at that setting, to not alleviate
241 * developers who will not understand why the emulator's colors
242 * look slightly too dark.
243 *
244 * we also want to implement a 'bright' mode by de-saturating
245 * colors towards bright white.
246 *
247 * All of this leads to the implementation below that looks like
248 * the following:
249 *
250 * if (level == MIN)
251 *     screen is off
252 *
253 * if (level > MIN && level < LOW)
254 *     interpolate towards black, with
255 *     MINALPHA = 0.2
256 *     alpha = MINALPHA + (1-MINALPHA)*(level-MIN)/(LOW-MIN)
257 *
258 * if (level >= LOW && level <= HIGH)
259 *     keep full RGB colors
260 *
261 * if (level > HIGH)
262 *     interpolate towards bright white, with
263 *     MAXALPHA = 0.6
264 *     alpha = MAXALPHA*(level-HIGH)/(MAX-HIGH)
265 *
266 * we probably want some sort of power law instead of interpolating
267 * linearly, but frankly, this is sufficient for most uses.
268 */
269
270#define  LCD_BRIGHTNESS_LOW   80
271#define  LCD_BRIGHTNESS_HIGH  180
272
273#define  LCD_ALPHA_LOW_MIN      0.2
274#define  LCD_ALPHA_HIGH_MAX     0.6
275
276/* treat as special value to turn screen off */
277#define  LCD_BRIGHTNESS_OFF   LCD_BRIGHTNESS_MIN
278
279static void
280lcd_brightness_argb32( unsigned char*  pixels, SkinRect*  r, int  pitch, int  brightness )
281{
282    const unsigned  b_min  = LCD_BRIGHTNESS_MIN;
283    const unsigned  b_max  = LCD_BRIGHTNESS_MAX;
284    const unsigned  b_low  = LCD_BRIGHTNESS_LOW;
285    const unsigned  b_high = LCD_BRIGHTNESS_HIGH;
286
287    unsigned        alpha = brightness;
288    int             w     = r->size.w;
289    int             h     = r->size.h;
290
291    if (alpha <= b_min)
292        alpha = b_min;
293    else if (alpha > b_max)
294        alpha = b_max;
295
296    pixels += 4*r->pos.x + r->pos.y*pitch;
297
298    if (alpha < b_low)
299    {
300        const unsigned  alpha_min   = (255*LCD_ALPHA_LOW_MIN);
301        const unsigned  alpha_range = (255 - alpha_min);
302
303        alpha = alpha_min + ((alpha - b_min)*alpha_range) / (b_low - b_min);
304
305        for ( ; h > 0; h-- ) {
306            unsigned*  line = (unsigned*) pixels;
307            int        nn;
308
309            for (nn = 0; nn < w; nn++) {
310                unsigned  c  = line[nn];
311                unsigned  ag = (c >> 8) & 0x00ff00ff;
312                unsigned  rb = (c)      & 0x00ff00ff;
313
314                ag = (ag*alpha)        & 0xff00ff00;
315                rb = ((rb*alpha) >> 8) & 0x00ff00ff;
316
317                line[nn] = (unsigned)(ag | rb);
318            }
319            pixels += pitch;
320        }
321    }
322    else if (alpha > LCD_BRIGHTNESS_HIGH) /* 'superluminous' mode */
323    {
324        const unsigned  alpha_max   = (255*LCD_ALPHA_HIGH_MAX);
325        const unsigned  alpha_range = (255-alpha_max);
326        unsigned        ialpha;
327
328        alpha  = ((alpha - b_high)*alpha_range) / (b_max - b_high);
329        ialpha = 255-alpha;
330
331        for ( ; h > 0; h-- ) {
332            unsigned*  line = (unsigned*) pixels;
333            int        nn;
334
335            for (nn = 0; nn < w; nn++) {
336                unsigned  c  = line[nn];
337                unsigned  ag = (c >> 8) & 0x00ff00ff;
338                unsigned  rb = (c)      & 0x00ff00ff;
339
340                /* interpolate towards bright white, i.e. 0x00ffffff */
341                ag = ((ag*ialpha + 0x00ff00ff*alpha)) & 0xff00ff00;
342                rb = ((rb*ialpha + 0x00ff00ff*alpha) >> 8) & 0x00ff00ff;
343
344                line[nn] = (unsigned)(ag | rb);
345            }
346            pixels += pitch;
347        }
348    }
349}
350
351
352/* this is called when the LCD framebuffer is off */
353static void
354lcd_off_argb32( unsigned char*  pixels, SkinRect*  r, int  pitch )
355{
356    int  x = r->pos.x;
357    int  y = r->pos.y;
358    int  w = r->size.w;
359    int  h = r->size.h;
360
361    pixels += 4*x + y*pitch;
362    for ( ; h > 0; h-- ) {
363        memset( pixels, 0, w*4 );
364        pixels += pitch;
365    }
366}
367
368
369static void
370display_redraw( ADisplay*  disp, SkinRect*  rect, SDL_Surface*  surface )
371{
372    SkinRect  r;
373
374    if (skin_rect_intersect( &r, rect, &disp->rect ))
375    {
376        int           x  = r.pos.x - disp->rect.pos.x;
377        int           y  = r.pos.y - disp->rect.pos.y;
378        int           w  = r.size.w;
379        int           h  = r.size.h;
380        int           disp_w    = disp->rect.size.w;
381        int           disp_h    = disp->rect.size.h;
382        int           dst_pitch = surface->pitch;
383        uint8_t*      dst_line  = (uint8_t*)surface->pixels + r.pos.x*4 + r.pos.y*dst_pitch;
384        int           src_pitch = disp->datasize.w*2;
385        uint8_t*      src_line  = (uint8_t*)disp->data;
386        int           yy, xx;
387#if 0
388        fprintf(stderr, "--- display redraw r.pos(%d,%d) r.size(%d,%d) "
389                        "disp.pos(%d,%d) disp.size(%d,%d) datasize(%d,%d) rect.pos(%d,%d) rect.size(%d,%d)\n",
390                        r.pos.x - disp->rect.pos.x, r.pos.y - disp->rect.pos.y, w, h, disp->rect.pos.x, disp->rect.pos.y,
391                        disp->rect.size.w, disp->rect.size.h, disp->datasize.w, disp->datasize.h,
392                        rect->pos.x, rect->pos.y, rect->size.w, rect->size.h );
393#endif
394        SDL_LockSurface( surface );
395
396        if (disp->brightness == LCD_BRIGHTNESS_OFF)
397        {
398            lcd_off_argb32( surface->pixels, &r, dst_pitch );
399        }
400        else
401        {
402            switch ( disp->rotation & 3 )
403            {
404            case ANDROID_ROTATION_0:
405                src_line += x*2 + y*src_pitch;
406
407                for (yy = h; yy > 0; yy--)
408                {
409                    uint32_t*  dst = (uint32_t*)dst_line;
410                    uint16_t*  src = (uint16_t*)src_line;
411
412                    for (xx = 0; xx < w; xx++) {
413                        dst[xx] = rgb565_to_argb32(src[xx]);
414                    }
415                    src_line += src_pitch;
416                    dst_line += dst_pitch;
417                }
418                break;
419
420            case ANDROID_ROTATION_90:
421                src_line += y*2 + (disp_w - x - 1)*src_pitch;
422
423                for (yy = h; yy > 0; yy--)
424                {
425                    uint32_t*  dst = (uint32_t*)dst_line;
426                    uint8_t*   src = src_line;
427
428                    for (xx = w; xx > 0; xx--)
429                    {
430                        dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]);
431                        src -= src_pitch;
432                        dst += 1;
433                    }
434                    src_line += 2;
435                    dst_line += dst_pitch;
436                }
437                break;
438
439            case ANDROID_ROTATION_180:
440                src_line += (disp_w -1 - x)*2 + (disp_h-1-y)*src_pitch;
441
442                for (yy = h; yy > 0; yy--)
443                {
444                    uint16_t*  src = (uint16_t*)src_line;
445                    uint32_t*  dst = (uint32_t*)dst_line;
446
447                    for (xx = w; xx > 0; xx--) {
448                        dst[0] = rgb565_to_argb32(src[0]);
449                        src -= 1;
450                        dst += 1;
451                    }
452
453                    src_line -= src_pitch;
454                    dst_line += dst_pitch;
455            }
456            break;
457
458            default:  /* ANDROID_ROTATION_270 */
459                src_line += (disp_h-1-y)*2 + x*src_pitch;
460
461                for (yy = h; yy > 0; yy--)
462                {
463                    uint32_t*  dst = (uint32_t*)dst_line;
464                    uint8_t*   src = src_line;
465
466                    for (xx = w; xx > 0; xx--) {
467                        dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]);
468                        dst   += 1;
469                        src   += src_pitch;
470                    }
471                    src_line -= 2;
472                    dst_line += dst_pitch;
473                }
474            }
475#if DOT_MATRIX
476            dotmatrix_dither_argb32( surface->pixels, r.pos.x, r.pos.y, r.size.w, r.size.h, surface->pitch );
477#endif
478            /* apply lightness */
479            lcd_brightness_argb32( surface->pixels, &r, surface->pitch, disp->brightness );
480        }
481        SDL_UnlockSurface( surface );
482
483        /* Apply onion skin */
484        if (disp->onion != NULL) {
485            SkinRect  r2;
486
487            if ( skin_rect_intersect( &r2, &r, &disp->onion_rect ) ) {
488                SDL_Rect  rs, rd;
489
490                rd.x = r2.pos.x;
491                rd.y = r2.pos.y;
492                rd.w = r2.size.w;
493                rd.h = r2.size.h;
494
495                rs.x = rd.x - disp->onion_rect.pos.x;
496                rs.y = rd.y - disp->onion_rect.pos.y;
497                rs.w = rd.w;
498                rs.h = rd.h;
499
500                SDL_BlitSurface( skin_image_surface(disp->onion), &rs, surface, &rd );
501            }
502        }
503
504        SDL_UpdateRect( surface, r.pos.x, r.pos.y, w, h );
505    }
506}
507
508
509typedef struct Button {
510    SkinImage*       image;
511    SkinRect         rect;
512    SkinPos          origin;
513    Background*      background;
514    unsigned         keycode;
515    int              down;
516} Button;
517
518static void
519button_done( Button*  button )
520{
521    skin_image_unref( &button->image );
522    button->background = NULL;
523}
524
525static void
526button_init( Button*  button, SkinButton*  sbutton, SkinLocation*  loc, Background*  back, SkinRect*  frame )
527{
528    SkinRect  r;
529
530    button->image      = skin_image_rotate( sbutton->image, loc->rotation );
531    button->background = back;
532    button->keycode    = sbutton->keycode;
533    button->down       = 0;
534
535    skin_rect_rotate( &r, &sbutton->rect, loc->rotation );
536    r.pos.x += loc->anchor.x;
537    r.pos.y += loc->anchor.y;
538    button->origin = r.pos;
539    skin_rect_intersect( &button->rect, &r, frame );
540}
541
542static void
543button_redraw( Button*  button, SkinRect*  rect, SDL_Surface*  surface )
544{
545    SkinRect  r;
546
547    if (skin_rect_intersect( &r, rect, &button->rect ))
548    {
549        if ( button->down && button->image != SKIN_IMAGE_NONE )
550        {
551            SDL_Rect  rs, rd;
552
553            rs.x = r.pos.x - button->origin.x;
554            rs.y = r.pos.y - button->origin.y;
555            rs.w = r.size.w;
556            rs.h = r.size.h;
557
558            rd.x = r.pos.x;
559            rd.y = r.pos.y;
560            rd.w = r.size.w;
561            rd.h = r.size.h;
562
563            if (button->image != SKIN_IMAGE_NONE) {
564                SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd );
565                if (button->down > 1)
566                    SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd );
567            }
568        }
569    }
570}
571
572
573typedef struct {
574    char      tracking;
575    char      inside;
576    SkinPos   pos;
577    ADisplay*  display;
578} FingerState;
579
580static void
581finger_state_reset( FingerState*  finger )
582{
583    finger->tracking = 0;
584    finger->inside   = 0;
585}
586
587typedef struct {
588    Button*   pressed;
589    Button*   hover;
590} ButtonState;
591
592static void
593button_state_reset( ButtonState*  button )
594{
595    button->pressed = NULL;
596    button->hover   = NULL;
597}
598
599typedef struct {
600    char            tracking;
601    SkinTrackBall*  ball;
602    SkinRect        rect;
603    SkinWindow*     window;
604} BallState;
605
606static void
607ball_state_reset( BallState*  state, SkinWindow*  window )
608{
609    state->tracking = 0;
610    state->ball     = NULL;
611
612    state->rect.pos.x  = 0;
613    state->rect.pos.y  = 0;
614    state->rect.size.w = 0;
615    state->rect.size.h = 0;
616    state->window      = window;
617}
618
619static void
620ball_state_redraw( BallState*  state, SkinRect*  rect, SDL_Surface*  surface )
621{
622    SkinRect  r;
623
624    if (skin_rect_intersect( &r, rect, &state->rect ))
625        skin_trackball_draw( state->ball, 0, 0, surface );
626}
627
628static void
629ball_state_show( BallState*  state, int  enable )
630{
631    if (enable) {
632        if ( !state->tracking ) {
633            state->tracking = 1;
634            SDL_ShowCursor(0);
635            SDL_WM_GrabInput( SDL_GRAB_ON );
636            skin_trackball_refresh( state->ball );
637            skin_window_redraw( state->window, &state->rect );
638        }
639    } else {
640        if ( state->tracking ) {
641            state->tracking = 0;
642            SDL_WM_GrabInput( SDL_GRAB_OFF );
643            SDL_ShowCursor(1);
644            skin_window_redraw( state->window, &state->rect );
645        }
646    }
647}
648
649
650static void
651ball_state_set( BallState*  state, SkinTrackBall*  ball )
652{
653    ball_state_show( state, 0 );
654
655    state->ball = ball;
656    if (ball != NULL) {
657        SDL_Rect  sr;
658
659        skin_trackball_rect( ball, &sr );
660        state->rect.pos.x  = sr.x;
661        state->rect.pos.y  = sr.y;
662        state->rect.size.w = sr.w;
663        state->rect.size.h = sr.h;
664    }
665}
666
667typedef struct Layout {
668    int          num_buttons;
669    int          num_backgrounds;
670    int          num_displays;
671    unsigned     color;
672    Button*      buttons;
673    Background*  backgrounds;
674    ADisplay*    displays;
675    SkinRect     rect;
676    SkinLayout*  slayout;
677} Layout;
678
679#define  LAYOUT_LOOP_BUTTONS(layout,button)                          \
680    do {                                                             \
681        Button*  __button = (layout)->buttons;                       \
682        Button*  __button_end = __button + (layout)->num_buttons;    \
683        for ( ; __button < __button_end; __button ++ ) {             \
684            Button*  button = __button;
685
686#define  LAYOUT_LOOP_END_BUTTONS \
687        }                        \
688    } while (0);
689
690#define  LAYOUT_LOOP_DISPLAYS(layout,display)                          \
691    do {                                                               \
692        ADisplay*  __display = (layout)->displays;                     \
693        ADisplay*  __display_end = __display + (layout)->num_displays; \
694        for ( ; __display < __display_end; __display ++ ) {            \
695            ADisplay*  display = __display;
696
697#define  LAYOUT_LOOP_END_DISPLAYS \
698        }                         \
699    } while (0);
700
701
702static void
703layout_done( Layout*  layout )
704{
705    int  nn;
706
707    for (nn = 0; nn < layout->num_buttons; nn++)
708        button_done( &layout->buttons[nn] );
709
710    for (nn = 0; nn < layout->num_backgrounds; nn++)
711        background_done( &layout->backgrounds[nn] );
712
713    for (nn = 0; nn < layout->num_displays; nn++)
714        display_done( &layout->displays[nn] );
715
716    qemu_free( layout->buttons );
717    layout->buttons = NULL;
718
719    qemu_free( layout->backgrounds );
720    layout->backgrounds = NULL;
721
722    qemu_free( layout->displays );
723    layout->displays = NULL;
724
725    layout->num_buttons     = 0;
726    layout->num_backgrounds = 0;
727    layout->num_displays    = 0;
728}
729
730static int
731layout_init( Layout*  layout, SkinLayout*  slayout )
732{
733    int       n_buttons, n_backgrounds, n_displays;
734
735    /* first, count the number of elements of each kind */
736    n_buttons     = 0;
737    n_backgrounds = 0;
738    n_displays    = 0;
739
740    layout->color   = slayout->color;
741    layout->slayout = slayout;
742
743    SKIN_LAYOUT_LOOP_LOCS(slayout,loc)
744        SkinPart*    part = loc->part;
745
746        if ( part->background->valid )
747            n_backgrounds += 1;
748        if ( part->display->valid )
749            n_displays += 1;
750
751        SKIN_PART_LOOP_BUTTONS(part, sbutton)
752            n_buttons += 1;
753            sbutton=sbutton;
754        SKIN_PART_LOOP_END
755    SKIN_LAYOUT_LOOP_END
756
757    layout->num_buttons     = n_buttons;
758    layout->num_backgrounds = n_backgrounds;
759    layout->num_displays    = n_displays;
760
761    /* now allocate arrays, then populate them */
762    layout->buttons     = qemu_mallocz( sizeof(Button) *     n_buttons );
763    layout->backgrounds = qemu_mallocz( sizeof(Background) * n_backgrounds );
764    layout->displays    = qemu_mallocz( sizeof(ADisplay) *    n_displays );
765
766    if (layout->buttons == NULL && n_buttons > 0) goto Fail;
767    if (layout->backgrounds == NULL && n_backgrounds > 0) goto Fail;
768    if (layout->displays == NULL && n_displays > 0) goto Fail;
769
770    n_buttons     = 0;
771    n_backgrounds = 0;
772    n_displays    = 0;
773
774    layout->rect.pos.x = 0;
775    layout->rect.pos.y = 0;
776    layout->rect.size  = slayout->size;
777
778    SKIN_LAYOUT_LOOP_LOCS(slayout,loc)
779        SkinPart*    part = loc->part;
780        Background*  back = NULL;
781
782        if ( part->background->valid ) {
783            back = layout->backgrounds + n_backgrounds;
784            background_init( back, part->background, loc, &layout->rect );
785            n_backgrounds += 1;
786        }
787        if ( part->display->valid ) {
788            ADisplay*  disp = layout->displays + n_displays;
789            display_init( disp, part->display, loc, &layout->rect );
790            n_displays += 1;
791        }
792
793        SKIN_PART_LOOP_BUTTONS(part, sbutton)
794            Button*  button = layout->buttons + n_buttons;
795            button_init( button, sbutton, loc, back, &layout->rect );
796            n_buttons += 1;
797        SKIN_PART_LOOP_END
798    SKIN_LAYOUT_LOOP_END
799
800    return 0;
801
802Fail:
803    layout_done(layout);
804    return -1;
805}
806
807struct SkinWindow {
808    SDL_Surface*  surface;
809    Layout        layout;
810    SkinPos       pos;
811    FingerState   finger;
812    ButtonState   button;
813    BallState     ball;
814    char          enabled;
815    char          fullscreen;
816    char          no_display;
817
818    char          enable_touch;
819    char          enable_trackball;
820    char          enable_dpad;
821    char          enable_qwerty;
822
823    SkinImage*    onion;
824    SkinRotation  onion_rotation;
825    int           onion_alpha;
826
827    int           x_pos;
828    int           y_pos;
829
830    SkinScaler*   scaler;
831    int           shrink;
832    double        shrink_scale;
833    unsigned*     shrink_pixels;
834    SDL_Surface*  shrink_surface;
835
836    double        effective_scale;
837    double        effective_x;
838    double        effective_y;
839};
840
841static void
842add_finger_event(unsigned x, unsigned y, unsigned state)
843{
844    //fprintf(stderr, "::: finger %d,%d %d\n", x, y, state);
845    kbd_mouse_event(x, y, 0, state);
846}
847
848static void
849skin_window_find_finger( SkinWindow*  window,
850                         int          x,
851                         int          y )
852{
853    FingerState*  finger = &window->finger;
854
855    /* find the display that contains this movement */
856    finger->display = NULL;
857    finger->inside  = 0;
858
859    if (!window->enable_touch)
860        return;
861
862    LAYOUT_LOOP_DISPLAYS(&window->layout,disp)
863        if ( skin_rect_contains( &disp->rect, x, y ) ) {
864            finger->inside   = 1;
865            finger->display  = disp;
866            finger->pos.x    = x - disp->origin.x;
867            finger->pos.y    = y - disp->origin.y;
868
869            skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation );
870            break;
871        }
872    LAYOUT_LOOP_END_DISPLAYS
873}
874
875static void
876skin_window_move_mouse( SkinWindow*  window,
877                        int          x,
878                        int          y )
879{
880    FingerState*  finger = &window->finger;
881    ButtonState*  button = &window->button;
882
883    if (finger->tracking) {
884        ADisplay*  disp   = finger->display;
885        char       inside = 1;
886        int        dx     = x - disp->rect.pos.x;
887        int        dy     = y - disp->rect.pos.y;
888
889        if (dx < 0) {
890            dx = 0;
891            inside = 0;
892        }
893        else if (dx >= disp->rect.size.w) {
894            dx = disp->rect.size.w - 1;
895            inside = 0;
896        }
897        if (dy < 0) {
898            dy = 0;
899            inside = 0;
900        } else if (dy >= disp->rect.size.h) {
901            dy = disp->rect.size.h-1;
902            inside = 0;
903        }
904        finger->inside = inside;
905        finger->pos.x  = dx + (disp->rect.pos.x - disp->origin.x);
906        finger->pos.y  = dy + (disp->rect.pos.y - disp->origin.y);
907
908        skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation );
909    }
910
911    {
912        Button*  hover = button->hover;
913
914        if (hover) {
915            if ( skin_rect_contains( &hover->rect, x, y ) )
916                return;
917
918            hover->down = 0;
919            skin_window_redraw( window, &hover->rect );
920            button->hover = NULL;
921        }
922
923        hover = NULL;
924        LAYOUT_LOOP_BUTTONS( &window->layout, butt )
925            if ( skin_rect_contains( &butt->rect, x, y ) ) {
926                hover = butt;
927                break;
928            }
929        LAYOUT_LOOP_END_BUTTONS
930
931        /* filter DPAD and QWERTY buttons right here */
932        if (hover != NULL) {
933            switch (hover->keycode) {
934                /* these correspond to the DPad */
935                case kKeyCodeDpadUp:
936                case kKeyCodeDpadDown:
937                case kKeyCodeDpadLeft:
938                case kKeyCodeDpadRight:
939                case kKeyCodeDpadCenter:
940                    if (!window->enable_dpad)
941                        hover = NULL;
942                    break;
943
944                /* these correspond to non-qwerty buttons */
945                case kKeyCodeSoftLeft:
946                case kKeyCodeSoftRight:
947                case kKeyCodeVolumeUp:
948                case kKeyCodeVolumeDown:
949                case kKeyCodePower:
950                case kKeyCodeHome:
951                case kKeyCodeBack:
952                case kKeyCodeCall:
953                case kKeyCodeEndCall:
954                    break;
955
956                /* all the rest is assumed to be qwerty */
957                default:
958                    if (!window->enable_qwerty)
959                        hover = NULL;
960            }
961        }
962
963        if (hover != NULL) {
964            hover->down = 1;
965            skin_window_redraw( window, &hover->rect );
966            button->hover = hover;
967        }
968    }
969}
970
971static void
972skin_window_trackball_press( SkinWindow*  window, int  down )
973{
974    send_key_event( kKeyCodeBtnMouse, down );
975}
976
977static void
978skin_window_trackball_move( SkinWindow*  window, int  xrel, int  yrel )
979{
980    BallState*  state = &window->ball;
981
982    if ( skin_trackball_move( state->ball, xrel, yrel ) ) {
983        skin_trackball_refresh( state->ball );
984        skin_window_redraw( window, &state->rect );
985    }
986}
987
988void
989skin_window_set_trackball( SkinWindow*  window, SkinTrackBall*  ball )
990{
991    BallState*  state = &window->ball;
992
993    ball_state_set( state, ball );
994}
995
996void
997skin_window_show_trackball( SkinWindow*  window, int  enable )
998{
999    BallState*  state = &window->ball;
1000
1001    if (state->ball != NULL && window->enable_trackball) {
1002        ball_state_show(state, enable);
1003    }
1004}
1005
1006
1007static int  skin_window_reset_internal (SkinWindow*, SkinLayout*);
1008
1009SkinWindow*
1010skin_window_create( SkinLayout*  slayout, int  x, int  y, double  scale, int  no_display )
1011{
1012    SkinWindow*  window = qemu_mallocz(sizeof(*window));
1013
1014    window->shrink_scale = scale;
1015    window->shrink       = (scale != 1.0);
1016    window->scaler       = skin_scaler_create();
1017    window->no_display   = no_display;
1018
1019    /* enable everything by default */
1020    window->enable_touch     = 1;
1021    window->enable_trackball = 1;
1022    window->enable_dpad      = 1;
1023    window->enable_qwerty    = 1;
1024
1025    window->x_pos = x;
1026    window->y_pos = y;
1027
1028    if (skin_window_reset_internal(window, slayout) < 0) {
1029        skin_window_free( window );
1030        return NULL;
1031    }
1032    //SDL_WM_SetCaption( "Android Emulator", "Android Emulator" );
1033
1034    SDL_WM_SetPos( x, y );
1035    if ( !SDL_WM_IsFullyVisible( 1 ) ) {
1036        dprint( "emulator window was out of view and was recentred\n" );
1037    }
1038
1039    return window;
1040}
1041
1042void
1043skin_window_enable_touch( SkinWindow*  window, int  enabled )
1044{
1045    window->enable_touch = !!enabled;
1046}
1047
1048void
1049skin_window_enable_trackball( SkinWindow*  window, int  enabled )
1050{
1051    window->enable_trackball = !!enabled;
1052}
1053
1054void
1055skin_window_enable_dpad( SkinWindow*  window, int  enabled )
1056{
1057    window->enable_dpad = !!enabled;
1058}
1059
1060void
1061skin_window_enable_qwerty( SkinWindow*  window, int  enabled )
1062{
1063    window->enable_qwerty = !!enabled;
1064}
1065
1066void
1067skin_window_set_title( SkinWindow*  window, const char*  title )
1068{
1069    if (window && title)
1070        SDL_WM_SetCaption( title, title );
1071}
1072
1073static void
1074skin_window_resize( SkinWindow*  window )
1075{
1076    /* now resize window */
1077    if (window->surface) {
1078        SDL_FreeSurface(window->surface);
1079        window->surface = NULL;
1080    }
1081
1082    if (window->shrink_surface) {
1083        SDL_FreeSurface(window->shrink_surface);
1084        window->shrink_surface = NULL;
1085    }
1086
1087    if (window->shrink_pixels) {
1088        qemu_free(window->shrink_pixels);
1089        window->shrink_pixels = NULL;
1090    }
1091
1092    if ( !window->no_display ) {
1093        int           layout_w = window->layout.rect.size.w;
1094        int           layout_h = window->layout.rect.size.h;
1095        int           window_w = layout_w;
1096        int           window_h = layout_h;
1097        int           window_x = window->x_pos;
1098        int           window_y = window->y_pos;
1099        int           flags;
1100        SDL_Surface*  surface;
1101        double        scale = 1.0;
1102        int           fullscreen = window->fullscreen;
1103
1104        if (fullscreen) {
1105            if (get_nearest_monitor_rect(&window_x, &window_y,
1106                                         &window_w, &window_h) < 0) {
1107                fullscreen = 0;
1108            } else {
1109                double  x_scale = window_w * 1.0 / layout_w;
1110                double  y_scale = window_h * 1.0 / layout_h;
1111
1112                scale = (x_scale <= y_scale) ? x_scale : y_scale;
1113            }
1114        }
1115        else if (window->shrink) {
1116            scale = window->shrink_scale;
1117            window_w = (int) ceil(layout_w*scale);
1118            window_h = (int) ceil(layout_h*scale);
1119        }
1120
1121        {
1122            char  temp[32];
1123            sprintf(temp,"SDL_VIDEO_WINDOW_POS=%d,%d",window_x,window_y);
1124            putenv(temp);
1125            putenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE=1");
1126        }
1127
1128        flags = SDL_SWSURFACE;
1129        if (fullscreen) {
1130            flags |= SDL_FULLSCREEN;
1131        }
1132        surface = SDL_SetVideoMode( window_w, window_h, 32, flags );
1133        if (surface == NULL) {
1134            fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
1135            exit(1);
1136        }
1137
1138        SDL_WM_SetPos( window_x, window_y );
1139
1140        window->effective_scale = scale;
1141        window->effective_x     = 0;
1142        window->effective_y     = 0;
1143
1144        if (fullscreen) {
1145            window->effective_x = (window_w - layout_w*scale)*0.5;
1146            window->effective_y = (window_h - layout_h*scale)*0.5;
1147        }
1148
1149        if (scale == 1.0)
1150            window->surface = surface;
1151        else
1152        {
1153            window_w = (int) ceil(window_w / scale );
1154            window_h = (int) ceil(window_h / scale );
1155
1156            window->shrink_surface = surface;
1157            window->shrink_pixels  = qemu_mallocz( window_w * window_h * 4 );
1158            if (window->shrink_pixels == NULL) {
1159                fprintf(stderr, "### Error: could not allocate memory for rescaling surface\n");
1160                exit(1);
1161            }
1162            window->surface = sdl_surface_from_argb32( window->shrink_pixels, window_w, window_h );
1163            if (window->surface == NULL) {
1164                fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
1165                exit(1);
1166            }
1167            skin_scaler_set( window->scaler, scale, window->effective_x, window->effective_y );
1168        }
1169    }
1170}
1171
1172static int
1173skin_window_reset_internal ( SkinWindow*  window, SkinLayout*  slayout )
1174{
1175    Layout         layout;
1176    ADisplay*      disp;
1177
1178    if ( layout_init( &layout, slayout ) < 0 )
1179        return -1;
1180
1181    disp = window->layout.displays;
1182
1183    layout_done( &window->layout );
1184    window->layout = layout;
1185
1186    disp = window->layout.displays;
1187    if (disp != NULL && window->onion)
1188        display_set_onion( disp,
1189                           window->onion,
1190                           window->onion_rotation,
1191                           window->onion_alpha );
1192
1193    skin_window_resize(window);
1194
1195    finger_state_reset( &window->finger );
1196    button_state_reset( &window->button );
1197    ball_state_reset( &window->ball, window );
1198
1199    skin_window_redraw( window, NULL );
1200
1201    if (slayout->event_type != 0) {
1202        kbd_generic_event( slayout->event_type, slayout->event_code, slayout->event_value );
1203    }
1204
1205    return 0;
1206}
1207
1208int
1209skin_window_reset ( SkinWindow*  window, SkinLayout*  slayout )
1210{
1211    if (!window->fullscreen) {
1212        SDL_WM_GetPos(&window->x_pos, &window->y_pos);
1213    }
1214    return skin_window_reset_internal( window, slayout );
1215}
1216
1217void
1218skin_window_set_lcd_brightness( SkinWindow*  window, int  brightness )
1219{
1220    ADisplay*  disp = window->layout.displays;
1221
1222    if (disp != NULL) {
1223        disp->brightness = brightness;
1224        skin_window_redraw( window, NULL );
1225    }
1226}
1227
1228void
1229skin_window_free  ( SkinWindow*  window )
1230{
1231    if (window) {
1232        if (window->surface) {
1233            SDL_FreeSurface(window->surface);
1234            window->surface = NULL;
1235        }
1236        if (window->shrink_surface) {
1237            SDL_FreeSurface(window->shrink_surface);
1238            window->shrink_surface = NULL;
1239        }
1240        if (window->shrink_pixels) {
1241            qemu_free(window->shrink_pixels);
1242            window->shrink_pixels = NULL;
1243        }
1244        if (window->onion) {
1245            skin_image_unref( &window->onion );
1246            window->onion_rotation = SKIN_ROTATION_0;
1247        }
1248        if (window->scaler) {
1249            skin_scaler_free(window->scaler);
1250            window->scaler = NULL;
1251        }
1252        layout_done( &window->layout );
1253        qemu_free(window);
1254    }
1255}
1256
1257void
1258skin_window_set_onion( SkinWindow*   window,
1259                       SkinImage*    onion,
1260                       SkinRotation  onion_rotation,
1261                       int           onion_alpha )
1262{
1263    ADisplay*  disp;
1264    SkinImage*  old = window->onion;
1265
1266    window->onion          = skin_image_ref(onion);
1267    window->onion_rotation = onion_rotation;
1268    window->onion_alpha    = onion_alpha;
1269
1270    skin_image_unref( &old );
1271
1272    disp = window->layout.displays;
1273
1274    if (disp != NULL)
1275        display_set_onion( disp, window->onion, onion_rotation, onion_alpha );
1276}
1277
1278static void
1279skin_window_update_shrink( SkinWindow*  window, SkinRect*  rect )
1280{
1281    skin_scaler_scale( window->scaler, window->shrink_surface, window->surface,
1282                       rect->pos.x, rect->pos.y, rect->size.w, rect->size.h );
1283}
1284
1285void
1286skin_window_set_scale( SkinWindow*  window, double  scale )
1287{
1288    window->shrink       = (scale != 1.0);
1289    window->shrink_scale = scale;
1290
1291    skin_window_resize( window );
1292    skin_window_redraw( window, NULL );
1293}
1294
1295void
1296skin_window_redraw( SkinWindow*  window, SkinRect*  rect )
1297{
1298    if (window != NULL && window->surface != NULL) {
1299        Layout*  layout = &window->layout;
1300
1301        if (rect == NULL)
1302            rect = &layout->rect;
1303
1304        {
1305            SkinRect  r;
1306
1307            if ( skin_rect_intersect( &r, rect, &layout->rect ) ) {
1308                SDL_Rect  rd;
1309                rd.x = r.pos.x;
1310                rd.y = r.pos.y;
1311                rd.w = r.size.w;
1312                rd.h = r.size.h;
1313
1314                SDL_FillRect( window->surface, &rd, layout->color );
1315            }
1316        }
1317
1318        {
1319            Background*  back = layout->backgrounds;
1320            Background*  end  = back + layout->num_backgrounds;
1321            for ( ; back < end; back++ )
1322                background_redraw( back, rect, window->surface );
1323        }
1324
1325        {
1326            ADisplay*  disp = layout->displays;
1327            ADisplay*  end  = disp + layout->num_displays;
1328            for ( ; disp < end; disp++ )
1329                display_redraw( disp, rect, window->surface );
1330        }
1331
1332        {
1333            Button*  button = layout->buttons;
1334            Button*  end    = button + layout->num_buttons;
1335            for ( ; button < end; button++ )
1336                button_redraw( button, rect, window->surface );
1337        }
1338
1339        if ( window->ball.tracking )
1340            ball_state_redraw( &window->ball, rect, window->surface );
1341
1342        if (window->effective_scale != 1.0)
1343            skin_window_update_shrink( window, rect );
1344        else
1345        {
1346            SDL_Rect  rd;
1347            rd.x = rect->pos.x;
1348            rd.y = rect->pos.y;
1349            rd.w = rect->size.w;
1350            rd.h = rect->size.h;
1351
1352            SDL_UpdateRects( window->surface, 1, &rd );
1353        }
1354    }
1355}
1356
1357void
1358skin_window_toggle_fullscreen( SkinWindow*  window )
1359{
1360    if (window && window->surface) {
1361        if (!window->fullscreen)
1362            SDL_WM_GetPos( &window->x_pos, &window->y_pos );
1363
1364        window->fullscreen = !window->fullscreen;
1365        skin_window_resize( window );
1366        skin_window_redraw( window, NULL );
1367    }
1368}
1369
1370void
1371skin_window_get_display( SkinWindow*  window, ADisplayInfo  *info )
1372{
1373    ADisplay*  disp = window->layout.displays;
1374
1375    if (disp != NULL) {
1376        info->width    = disp->datasize.w;
1377        info->height   = disp->datasize.h;
1378        info->rotation = disp->rotation;
1379        info->data     = disp->data;
1380    } else {
1381        info->width    = 0;
1382        info->height   = 0;
1383        info->rotation = SKIN_ROTATION_0;
1384        info->data     = NULL;
1385    }
1386}
1387
1388
1389static void
1390skin_window_map_to_scale( SkinWindow*  window, int  *x, int  *y )
1391{
1392    *x = (*x - window->effective_x) / window->effective_scale;
1393    *y = (*y - window->effective_y) / window->effective_scale;
1394}
1395
1396void
1397skin_window_process_event( SkinWindow*  window, SDL_Event*  ev )
1398{
1399    Button*  button;
1400    int      mx, my;
1401
1402    if (!window->surface)
1403        return;
1404
1405    switch (ev->type) {
1406    case SDL_MOUSEBUTTONDOWN:
1407        if ( window->ball.tracking ) {
1408            skin_window_trackball_press( window, 1 );
1409            break;
1410        }
1411
1412        mx = ev->button.x;
1413        my = ev->button.y;
1414        skin_window_map_to_scale( window, &mx, &my );
1415        skin_window_move_mouse( window, mx, my );
1416        skin_window_find_finger( window, mx, my );
1417#if 0
1418        printf("down: x=%d y=%d fx=%d fy=%d fis=%d\n",
1419               ev->button.x, ev->button.y, window->finger.pos.x,
1420               window->finger.pos.y, window->finger.inside);
1421#endif
1422        if (window->finger.inside) {
1423            window->finger.tracking = 1;
1424            add_finger_event(window->finger.pos.x, window->finger.pos.y, 1);
1425        } else {
1426            window->button.pressed = NULL;
1427            button = window->button.hover;
1428            if(button) {
1429                button->down += 1;
1430                skin_window_redraw( window, &button->rect );
1431                window->button.pressed = button;
1432                if(button->keycode) {
1433                    send_key_event(button->keycode, 1);
1434                }
1435            }
1436        }
1437        break;
1438
1439    case SDL_MOUSEBUTTONUP:
1440        if ( window->ball.tracking ) {
1441            skin_window_trackball_press( window, 0 );
1442            break;
1443        }
1444        button = window->button.pressed;
1445        mx = ev->button.x;
1446        my = ev->button.y;
1447        skin_window_map_to_scale( window, &mx, &my );
1448        if (button)
1449        {
1450            button->down = 0;
1451            skin_window_redraw( window, &button->rect );
1452            if(button->keycode) {
1453                send_key_event(button->keycode, 0);
1454            }
1455            window->button.pressed = NULL;
1456            window->button.hover   = NULL;
1457            skin_window_move_mouse( window, mx, my );
1458        }
1459        else if (window->finger.tracking)
1460        {
1461            skin_window_move_mouse( window, mx, my );
1462            window->finger.tracking = 0;
1463            add_finger_event( window->finger.pos.x, window->finger.pos.y, 0);
1464        }
1465        break;
1466
1467    case SDL_MOUSEMOTION:
1468        if ( window->ball.tracking ) {
1469            skin_window_trackball_move( window, ev->motion.xrel, ev->motion.yrel );
1470            break;
1471        }
1472        mx = ev->button.x;
1473        my = ev->button.y;
1474        skin_window_map_to_scale( window, &mx, &my );
1475        if ( !window->button.pressed )
1476        {
1477            skin_window_move_mouse( window, mx, my );
1478            if ( window->finger.tracking ) {
1479                add_finger_event( window->finger.pos.x, window->finger.pos.y, 1 );
1480            }
1481        }
1482        break;
1483    }
1484}
1485
1486static ADisplay*
1487skin_window_display( SkinWindow*  window )
1488{
1489    return window->layout.displays;
1490}
1491
1492void
1493skin_window_update_display( SkinWindow*  window, int  x, int  y, int  w, int  h )
1494{
1495    ADisplay*  disp = skin_window_display(window);
1496
1497    if ( !window->surface )
1498        return;
1499
1500    if (disp != NULL) {
1501        SkinRect  r;
1502        r.pos.x  = x;
1503        r.pos.y  = y;
1504        r.size.w = w;
1505        r.size.h = h;
1506
1507        skin_rect_rotate( &r, &r, disp->rotation );
1508        r.pos.x += disp->origin.x;
1509        r.pos.y += disp->origin.y;
1510
1511        if (window->effective_scale != 1.0)
1512            skin_window_redraw( window, &r );
1513        else
1514            display_redraw( disp, &r, window->surface );
1515    }
1516}
1517