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