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