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