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