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