window.c revision a46c1675f90288252ae0e992f52c72a6db5dd2a7
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/ui-core-protocol.h"
20#include <SDL_syswm.h>
21#include "user-events.h"
22#include <math.h>
23
24#include "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    ANEW0(window);
1138
1139    window->shrink_scale = scale;
1140    window->shrink       = (scale != 1.0);
1141    window->scaler       = skin_scaler_create();
1142    window->no_display   = no_display;
1143
1144    /* enable everything by default */
1145    window->enable_touch     = 1;
1146    window->enable_trackball = 1;
1147    window->enable_dpad      = 1;
1148    window->enable_qwerty    = 1;
1149
1150    window->x_pos = x;
1151    window->y_pos = y;
1152
1153    if (skin_window_reset_internal(window, slayout) < 0) {
1154        skin_window_free( window );
1155        return NULL;
1156    }
1157    //SDL_WM_SetCaption( "Android Emulator", "Android Emulator" );
1158
1159    SDL_WM_SetPos( x, y );
1160    if ( !SDL_WM_IsFullyVisible( 1 ) ) {
1161        dprint( "emulator window was out of view and was recentred\n" );
1162    }
1163
1164    return window;
1165}
1166
1167void
1168skin_window_enable_touch( SkinWindow*  window, int  enabled )
1169{
1170    window->enable_touch = !!enabled;
1171}
1172
1173void
1174skin_window_enable_trackball( SkinWindow*  window, int  enabled )
1175{
1176    window->enable_trackball = !!enabled;
1177}
1178
1179void
1180skin_window_enable_dpad( SkinWindow*  window, int  enabled )
1181{
1182    window->enable_dpad = !!enabled;
1183}
1184
1185void
1186skin_window_enable_qwerty( SkinWindow*  window, int  enabled )
1187{
1188    window->enable_qwerty = !!enabled;
1189}
1190
1191void
1192skin_window_set_title( SkinWindow*  window, const char*  title )
1193{
1194    if (window && title)
1195        SDL_WM_SetCaption( title, title );
1196}
1197
1198static void
1199skin_window_resize( SkinWindow*  window )
1200{
1201    /* now resize window */
1202    if (window->surface) {
1203        SDL_FreeSurface(window->surface);
1204        window->surface = NULL;
1205    }
1206
1207    if (window->shrink_surface) {
1208        SDL_FreeSurface(window->shrink_surface);
1209        window->shrink_surface = NULL;
1210    }
1211
1212    if (window->shrink_pixels) {
1213        AFREE(window->shrink_pixels);
1214        window->shrink_pixels = NULL;
1215    }
1216
1217    if ( !window->no_display ) {
1218        int           layout_w = window->layout.rect.size.w;
1219        int           layout_h = window->layout.rect.size.h;
1220        int           window_w = layout_w;
1221        int           window_h = layout_h;
1222        int           window_x = window->x_pos;
1223        int           window_y = window->y_pos;
1224        int           flags;
1225        SDL_Surface*  surface;
1226        double        scale = 1.0;
1227        int           fullscreen = window->fullscreen;
1228
1229        if (fullscreen) {
1230            SDL_Rect  r;
1231            if (SDL_WM_GetMonitorRect(&r) < 0) {
1232                fullscreen = 0;
1233            } else {
1234                double  x_scale, y_scale;
1235
1236                window_x = r.x;
1237                window_y = r.y;
1238                window_w = r.w;
1239                window_h = r.h;
1240
1241                x_scale = window_w * 1.0 / layout_w;
1242                y_scale = window_h * 1.0 / layout_h;
1243
1244                scale = (x_scale <= y_scale) ? x_scale : y_scale;
1245            }
1246        }
1247        else if (window->shrink) {
1248            scale = window->shrink_scale;
1249            window_w = (int) ceil(layout_w*scale);
1250            window_h = (int) ceil(layout_h*scale);
1251        }
1252
1253        {
1254            char temp[32];
1255#ifdef HAVE_SETENV
1256            sprintf(temp, "%d,%d", window_x, window_y);
1257            setenv("SDL_VIDEO_WINDOW_POS", temp, 1);
1258#else
1259            sprintf(temp,"SDL_VIDEO_WINDOW_POS=%d,%d",window_x,window_y);
1260            putenv(temp);
1261#endif
1262            putenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE=1");
1263        }
1264
1265        flags = SDL_SWSURFACE;
1266        if (fullscreen) {
1267            flags |= SDL_FULLSCREEN;
1268        }
1269        surface = SDL_SetVideoMode( window_w, window_h, 32, flags );
1270        if (surface == NULL) {
1271            fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
1272            exit(1);
1273        }
1274
1275        SDL_WM_SetPos( window_x, window_y );
1276
1277        window->effective_scale = scale;
1278        window->effective_x     = 0;
1279        window->effective_y     = 0;
1280
1281        if (fullscreen) {
1282            window->effective_x = (window_w - layout_w*scale)*0.5;
1283            window->effective_y = (window_h - layout_h*scale)*0.5;
1284        }
1285
1286        if (scale == 1.0)
1287            window->surface = surface;
1288        else
1289        {
1290            window_w = (int) ceil(window_w / scale );
1291            window_h = (int) ceil(window_h / scale );
1292
1293            window->shrink_surface = surface;
1294            AARRAY_NEW0(window->shrink_pixels, window_w * window_h * 4);
1295            if (window->shrink_pixels == NULL) {
1296                fprintf(stderr, "### Error: could not allocate memory for rescaling surface\n");
1297                exit(1);
1298            }
1299            window->surface = sdl_surface_from_argb32( window->shrink_pixels, window_w, window_h );
1300            if (window->surface == NULL) {
1301                fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
1302                exit(1);
1303            }
1304            skin_scaler_set( window->scaler, scale, window->effective_x, window->effective_y );
1305        }
1306    }
1307}
1308
1309static int
1310skin_window_reset_internal ( SkinWindow*  window, SkinLayout*  slayout )
1311{
1312    Layout         layout;
1313    ADisplay*      disp;
1314
1315    if ( layout_init( &layout, slayout ) < 0 )
1316        return -1;
1317
1318    disp = window->layout.displays;
1319
1320    layout_done( &window->layout );
1321    window->layout = layout;
1322
1323    disp = window->layout.displays;
1324    if (disp != NULL && window->onion)
1325        display_set_onion( disp,
1326                           window->onion,
1327                           window->onion_rotation,
1328                           window->onion_alpha );
1329
1330    skin_window_resize(window);
1331
1332    finger_state_reset( &window->finger );
1333    button_state_reset( &window->button );
1334    ball_state_reset( &window->ball, window );
1335
1336    skin_window_redraw( window, NULL );
1337
1338    if (slayout->event_type != 0) {
1339        user_event_generic( slayout->event_type, slayout->event_code, slayout->event_value );
1340        /* XXX: hack, replace by better code here */
1341        if (slayout->event_value != 0)
1342            android_core_sensors_set_coarse_orientation( ANDROID_COARSE_PORTRAIT );
1343        else
1344            android_core_sensors_set_coarse_orientation( ANDROID_COARSE_LANDSCAPE );
1345    }
1346
1347    return 0;
1348}
1349
1350int
1351skin_window_reset ( SkinWindow*  window, SkinLayout*  slayout )
1352{
1353    if (!window->fullscreen) {
1354        SDL_WM_GetPos(&window->x_pos, &window->y_pos);
1355    }
1356    if (skin_window_reset_internal( window, slayout ) < 0)
1357        return -1;
1358
1359    return 0;
1360}
1361
1362void
1363skin_window_set_lcd_brightness( SkinWindow*  window, int  brightness )
1364{
1365    ADisplay*  disp = window->layout.displays;
1366
1367    if (disp != NULL) {
1368        disp->brightness = brightness;
1369        skin_window_redraw( window, NULL );
1370    }
1371}
1372
1373void
1374skin_window_free  ( SkinWindow*  window )
1375{
1376    if (window) {
1377        if (window->surface) {
1378            SDL_FreeSurface(window->surface);
1379            window->surface = NULL;
1380        }
1381        if (window->shrink_surface) {
1382            SDL_FreeSurface(window->shrink_surface);
1383            window->shrink_surface = NULL;
1384        }
1385        if (window->shrink_pixels) {
1386            AFREE(window->shrink_pixels);
1387            window->shrink_pixels = NULL;
1388        }
1389        if (window->onion) {
1390            skin_image_unref( &window->onion );
1391            window->onion_rotation = SKIN_ROTATION_0;
1392        }
1393        if (window->scaler) {
1394            skin_scaler_free(window->scaler);
1395            window->scaler = NULL;
1396        }
1397        layout_done( &window->layout );
1398        AFREE(window);
1399    }
1400}
1401
1402void
1403skin_window_set_onion( SkinWindow*   window,
1404                       SkinImage*    onion,
1405                       SkinRotation  onion_rotation,
1406                       int           onion_alpha )
1407{
1408    ADisplay*  disp;
1409    SkinImage*  old = window->onion;
1410
1411    window->onion          = skin_image_ref(onion);
1412    window->onion_rotation = onion_rotation;
1413    window->onion_alpha    = onion_alpha;
1414
1415    skin_image_unref( &old );
1416
1417    disp = window->layout.displays;
1418
1419    if (disp != NULL)
1420        display_set_onion( disp, window->onion, onion_rotation, onion_alpha );
1421}
1422
1423static void
1424skin_window_update_shrink( SkinWindow*  window, SkinRect*  rect )
1425{
1426    skin_scaler_scale( window->scaler, window->shrink_surface, window->surface,
1427                       rect->pos.x, rect->pos.y, rect->size.w, rect->size.h );
1428}
1429
1430void
1431skin_window_set_scale( SkinWindow*  window, double  scale )
1432{
1433    window->shrink       = (scale != 1.0);
1434    window->shrink_scale = scale;
1435
1436    skin_window_resize( window );
1437    skin_window_redraw( window, NULL );
1438}
1439
1440void
1441skin_window_redraw( SkinWindow*  window, SkinRect*  rect )
1442{
1443    if (window != NULL && window->surface != NULL) {
1444        Layout*  layout = &window->layout;
1445
1446        if (rect == NULL)
1447            rect = &layout->rect;
1448
1449        {
1450            SkinRect  r;
1451
1452            if ( skin_rect_intersect( &r, rect, &layout->rect ) ) {
1453                SDL_Rect  rd;
1454                rd.x = r.pos.x;
1455                rd.y = r.pos.y;
1456                rd.w = r.size.w;
1457                rd.h = r.size.h;
1458
1459                SDL_FillRect( window->surface, &rd, layout->color );
1460            }
1461        }
1462
1463        {
1464            Background*  back = layout->backgrounds;
1465            Background*  end  = back + layout->num_backgrounds;
1466            for ( ; back < end; back++ )
1467                background_redraw( back, rect, window->surface );
1468        }
1469
1470        {
1471            ADisplay*  disp = layout->displays;
1472            ADisplay*  end  = disp + layout->num_displays;
1473            for ( ; disp < end; disp++ )
1474                display_redraw( disp, rect, window->surface );
1475        }
1476
1477        {
1478            Button*  button = layout->buttons;
1479            Button*  end    = button + layout->num_buttons;
1480            for ( ; button < end; button++ )
1481                button_redraw( button, rect, window->surface );
1482        }
1483
1484        if ( window->ball.tracking )
1485            ball_state_redraw( &window->ball, rect, window->surface );
1486
1487        if (window->effective_scale != 1.0)
1488            skin_window_update_shrink( window, rect );
1489        else
1490        {
1491            SDL_Rect  rd;
1492            rd.x = rect->pos.x;
1493            rd.y = rect->pos.y;
1494            rd.w = rect->size.w;
1495            rd.h = rect->size.h;
1496
1497            SDL_UpdateRects( window->surface, 1, &rd );
1498        }
1499    }
1500}
1501
1502void
1503skin_window_toggle_fullscreen( SkinWindow*  window )
1504{
1505    if (window && window->surface) {
1506        if (!window->fullscreen)
1507            SDL_WM_GetPos( &window->x_pos, &window->y_pos );
1508
1509        window->fullscreen = !window->fullscreen;
1510        skin_window_resize( window );
1511        skin_window_redraw( window, NULL );
1512    }
1513}
1514
1515void
1516skin_window_get_display( SkinWindow*  window, ADisplayInfo  *info )
1517{
1518    ADisplay*  disp = window->layout.displays;
1519
1520    if (disp != NULL) {
1521        info->width    = disp->datasize.w;
1522        info->height   = disp->datasize.h;
1523        info->rotation = disp->rotation;
1524        info->data     = disp->data;
1525    } else {
1526        info->width    = 0;
1527        info->height   = 0;
1528        info->rotation = SKIN_ROTATION_0;
1529        info->data     = NULL;
1530    }
1531}
1532
1533
1534static void
1535skin_window_map_to_scale( SkinWindow*  window, int  *x, int  *y )
1536{
1537    *x = (*x - window->effective_x) / window->effective_scale;
1538    *y = (*y - window->effective_y) / window->effective_scale;
1539}
1540
1541void
1542skin_window_process_event( SkinWindow*  window, SDL_Event*  ev )
1543{
1544    Button*  button;
1545    int      mx, my;
1546
1547    if (!window->surface)
1548        return;
1549
1550    switch (ev->type) {
1551    case SDL_MOUSEBUTTONDOWN:
1552        if ( window->ball.tracking ) {
1553            skin_window_trackball_press( window, 1 );
1554            break;
1555        }
1556
1557        mx = ev->button.x;
1558        my = ev->button.y;
1559        skin_window_map_to_scale( window, &mx, &my );
1560        skin_window_move_mouse( window, mx, my );
1561        skin_window_find_finger( window, mx, my );
1562#if 0
1563        printf("down: x=%d y=%d fx=%d fy=%d fis=%d\n",
1564               ev->button.x, ev->button.y, window->finger.pos.x,
1565               window->finger.pos.y, window->finger.inside);
1566#endif
1567        if (window->finger.inside) {
1568            window->finger.tracking = 1;
1569            add_finger_event(window->finger.pos.x, window->finger.pos.y, 1);
1570        } else {
1571            window->button.pressed = NULL;
1572            button = window->button.hover;
1573            if(button) {
1574                button->down += 1;
1575                skin_window_redraw( window, &button->rect );
1576                window->button.pressed = button;
1577                if(button->keycode) {
1578                    user_event_key(button->keycode, 1);
1579                }
1580            }
1581        }
1582        break;
1583
1584    case SDL_MOUSEBUTTONUP:
1585        if ( window->ball.tracking ) {
1586            skin_window_trackball_press( window, 0 );
1587            break;
1588        }
1589        button = window->button.pressed;
1590        mx = ev->button.x;
1591        my = ev->button.y;
1592        skin_window_map_to_scale( window, &mx, &my );
1593        if (button)
1594        {
1595            button->down = 0;
1596            skin_window_redraw( window, &button->rect );
1597            if(button->keycode) {
1598                user_event_key(button->keycode, 0);
1599            }
1600            window->button.pressed = NULL;
1601            window->button.hover   = NULL;
1602            skin_window_move_mouse( window, mx, my );
1603        }
1604        else if (window->finger.tracking)
1605        {
1606            skin_window_move_mouse( window, mx, my );
1607            window->finger.tracking = 0;
1608            add_finger_event( window->finger.pos.x, window->finger.pos.y, 0);
1609        }
1610        break;
1611
1612    case SDL_MOUSEMOTION:
1613        if ( window->ball.tracking ) {
1614            skin_window_trackball_move( window, ev->motion.xrel, ev->motion.yrel );
1615            break;
1616        }
1617        mx = ev->button.x;
1618        my = ev->button.y;
1619        skin_window_map_to_scale( window, &mx, &my );
1620        if ( !window->button.pressed )
1621        {
1622            skin_window_move_mouse( window, mx, my );
1623            if ( window->finger.tracking ) {
1624                add_finger_event( window->finger.pos.x, window->finger.pos.y, 1 );
1625            }
1626        }
1627        break;
1628    }
1629}
1630
1631static ADisplay*
1632skin_window_display( SkinWindow*  window )
1633{
1634    return window->layout.displays;
1635}
1636
1637void
1638skin_window_update_display( SkinWindow*  window, int  x, int  y, int  w, int  h )
1639{
1640    ADisplay*  disp = skin_window_display(window);
1641
1642    if ( !window->surface )
1643        return;
1644
1645    if (disp != NULL) {
1646        SkinRect  r;
1647        r.pos.x  = x;
1648        r.pos.y  = y;
1649        r.size.w = w;
1650        r.size.h = h;
1651
1652        skin_rect_rotate( &r, &r, disp->rotation );
1653        r.pos.x += disp->origin.x;
1654        r.pos.y += disp->origin.y;
1655
1656        if (window->effective_scale != 1.0)
1657            skin_window_redraw( window, &r );
1658        else
1659            display_redraw( disp, &r, window->surface );
1660    }
1661}
1662