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