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