diff options
Diffstat (limited to 'android/skin/window.c')
-rw-r--r-- | android/skin/window.c | 1516 |
1 files changed, 1516 insertions, 0 deletions
diff --git a/android/skin/window.c b/android/skin/window.c new file mode 100644 index 0000000..6612ffe --- /dev/null +++ b/android/skin/window.c @@ -0,0 +1,1516 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/skin/window.h" +#include "android/skin/image.h" +#include "android/skin/scaler.h" +#include "android/charmap.h" +#include "android/utils/debug.h" +#include "android/utils/display.h" +#include <SDL_syswm.h> +#include "qemu-common.h" +#include <math.h> + +#include "framebuffer.h" + +/* when shrinking, we reduce the pixel ratio by this fixed amount */ +#define SHRINK_SCALE 0.6 + +/* maximum value of LCD brighness */ +#define LCD_BRIGHTNESS_MIN 0 +#define LCD_BRIGHTNESS_DEFAULT 128 +#define LCD_BRIGHTNESS_MAX 255 + +typedef struct Background { + SkinImage* image; + SkinRect rect; + SkinPos origin; +} Background; + +static void +background_done( Background* back ) +{ + skin_image_unref( &back->image ); +} + +static void +background_init( Background* back, SkinBackground* sback, SkinLocation* loc, SkinRect* frame ) +{ + SkinRect r; + + back->image = skin_image_rotate( sback->image, loc->rotation ); + skin_rect_rotate( &r, &sback->rect, loc->rotation ); + r.pos.x += loc->anchor.x; + r.pos.y += loc->anchor.y; + + back->origin = r.pos; + skin_rect_intersect( &back->rect, &r, frame ); +} + +static void +background_redraw( Background* back, SkinRect* rect, SDL_Surface* surface ) +{ + SkinRect r; + + if (skin_rect_intersect( &r, rect, &back->rect ) ) + { + SDL_Rect rd, rs; + + rd.x = r.pos.x; + rd.y = r.pos.y; + rd.w = r.size.w; + rd.h = r.size.h; + + rs.x = r.pos.x - back->origin.x; + rs.y = r.pos.y - back->origin.y; + rs.w = r.size.w; + rs.h = r.size.h; + + SDL_BlitSurface( skin_image_surface(back->image), &rs, surface, &rd ); + //SDL_UpdateRects( surface, 1, &rd ); + } +} + + +typedef struct ADisplay { + SkinRect rect; + SkinPos origin; + SkinRotation rotation; + SkinSize datasize; /* framebuffer size */ + void* data; /* framebuffer pixels */ + QFrameBuffer* qfbuff; + SkinImage* onion; /* onion image */ + SkinRect onion_rect; /* onion rect, if any */ + int brightness; +} ADisplay; + +static void +display_done( ADisplay* disp ) +{ + disp->data = NULL; + disp->qfbuff = NULL; + skin_image_unref( &disp->onion ); +} + +static int +display_init( ADisplay* disp, SkinDisplay* sdisp, SkinLocation* loc, SkinRect* frame ) +{ + skin_rect_rotate( &disp->rect, &sdisp->rect, loc->rotation ); + disp->rect.pos.x += loc->anchor.x; + disp->rect.pos.y += loc->anchor.y; + + disp->rotation = (loc->rotation + sdisp->rotation) & 3; + switch (disp->rotation) { + case SKIN_ROTATION_0: + disp->origin = disp->rect.pos; + break; + + case SKIN_ROTATION_90: + disp->origin.x = disp->rect.pos.x + disp->rect.size.w; + disp->origin.y = disp->rect.pos.y; + break; + + case SKIN_ROTATION_180: + disp->origin.x = disp->rect.pos.x + disp->rect.size.w; + disp->origin.y = disp->rect.pos.y + disp->rect.size.h; + break; + + default: + disp->origin.x = disp->rect.pos.x; + disp->origin.y = disp->rect.pos.y + disp->rect.size.h; + break; + } + skin_size_rotate( &disp->datasize, &sdisp->rect.size, sdisp->rotation ); + skin_rect_intersect( &disp->rect, &disp->rect, frame ); +#if 0 + fprintf(stderr, "... display_init rect.pos(%d,%d) rect.size(%d,%d) datasize(%d,%d)\n", + disp->rect.pos.x, disp->rect.pos.y, + disp->rect.size.w, disp->rect.size.h, + disp->datasize.w, disp->datasize.h); +#endif + disp->qfbuff = sdisp->qfbuff; + disp->data = sdisp->qfbuff->pixels; + disp->onion = NULL; + + disp->brightness = LCD_BRIGHTNESS_DEFAULT; + + return (disp->data == NULL) ? -1 : 0; +} + +static __inline__ uint32_t rgb565_to_argb32( uint32_t pix ) +{ + uint32_t r = ((pix & 0xf800) << 8) | ((pix & 0xe000) << 3); + uint32_t g = ((pix & 0x07e0) << 5) | ((pix & 0x0600) >> 1); + uint32_t b = ((pix & 0x001f) << 3) | ((pix & 0x001c) >> 2); + + return 0xff000000 | r | g | b; +} + + +static void +display_set_onion( ADisplay* disp, SkinImage* onion, SkinRotation rotation, int blend ) +{ + int onion_w, onion_h; + SkinRect* rect = &disp->rect; + SkinRect* orect = &disp->onion_rect; + + rotation = (rotation + disp->rotation) & 3; + + skin_image_unref( &disp->onion ); + disp->onion = skin_image_clone_full( onion, rotation, blend ); + + onion_w = skin_image_w(disp->onion); + onion_h = skin_image_h(disp->onion); + + switch (rotation) { + case SKIN_ROTATION_0: + orect->pos = rect->pos; + break; + + case SKIN_ROTATION_90: + orect->pos.x = rect->pos.x + rect->size.w - onion_w; + orect->pos.y = rect->pos.y; + break; + + case SKIN_ROTATION_180: + orect->pos.x = rect->pos.x + rect->size.w - onion_w; + orect->pos.y = rect->pos.y + rect->size.h - onion_h; + break; + + default: + orect->pos.x = rect->pos.x; + orect->pos.y = rect->pos.y + rect->size.h - onion_h; + } + orect->size.w = onion_w; + orect->size.h = onion_h; +} + +#define DOT_MATRIX 0 + +#if DOT_MATRIX + +static void +dotmatrix_dither_argb32( unsigned char* pixels, int x, int y, int w, int h, int pitch ) +{ + static const unsigned dotmatrix_argb32[16] = { + 0x003f00, 0x00003f, 0x3f0000, 0x000000, + 0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000, + 0x3f0000, 0x000000, 0x003f00, 0x00003f, + 0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000 + }; + + int yy = y & 3; + + pixels += 4*x + y*pitch; + + for ( ; h > 0; h-- ) { + unsigned* line = (unsigned*) pixels; + int nn, xx = x & 3; + + for (nn = 0; nn < w; nn++) { + unsigned c = line[nn]; + + c = c - ((c >> 2) & dotmatrix_argb32[(yy << 2)|xx]); + + xx = (xx + 1) & 3; + line[nn] = c; + } + + yy = (yy + 1) & 3; + pixels += pitch; + } +} + +#endif /* DOT_MATRIX */ + +/* technical note about the lightness emulation + * + * we try to emulate something that looks like the Dream's + * non-linear LCD lightness, without going too dark or bright. + * + * the default lightness is around 105 (about 40%) and we prefer + * to keep full RGB colors at that setting, to not alleviate + * developers who will not understand why the emulator's colors + * look slightly too dark. + * + * we also want to implement a 'bright' mode by de-saturating + * colors towards bright white. + * + * All of this leads to the implementation below that looks like + * the following: + * + * if (level == MIN) + * screen is off + * + * if (level > MIN && level < LOW) + * interpolate towards black, with + * MINALPHA = 0.2 + * alpha = MINALPHA + (1-MINALPHA)*(level-MIN)/(LOW-MIN) + * + * if (level >= LOW && level <= HIGH) + * keep full RGB colors + * + * if (level > HIGH) + * interpolate towards bright white, with + * MAXALPHA = 0.6 + * alpha = MAXALPHA*(level-HIGH)/(MAX-HIGH) + * + * we probably want some sort of power law instead of interpolating + * linearly, but frankly, this is sufficient for most uses. + */ + +#define LCD_BRIGHTNESS_LOW 80 +#define LCD_BRIGHTNESS_HIGH 180 + +#define LCD_ALPHA_LOW_MIN 0.2 +#define LCD_ALPHA_HIGH_MAX 0.6 + +/* treat as special value to turn screen off */ +#define LCD_BRIGHTNESS_OFF LCD_BRIGHTNESS_MIN + +static void +lcd_brightness_argb32( unsigned char* pixels, SkinRect* r, int pitch, int brightness ) +{ + const unsigned b_min = LCD_BRIGHTNESS_MIN; + const unsigned b_max = LCD_BRIGHTNESS_MAX; + const unsigned b_low = LCD_BRIGHTNESS_LOW; + const unsigned b_high = LCD_BRIGHTNESS_HIGH; + + unsigned alpha = brightness; + int w = r->size.w; + int h = r->size.h; + + if (alpha <= b_min) + alpha = b_min; + else if (alpha > b_max) + alpha = b_max; + + pixels += 4*r->pos.x + r->pos.y*pitch; + + if (alpha < b_low) + { + const unsigned alpha_min = (255*LCD_ALPHA_LOW_MIN); + const unsigned alpha_range = (255 - alpha_min); + + alpha = alpha_min + ((alpha - b_min)*alpha_range) / (b_low - b_min); + + for ( ; h > 0; h-- ) { + unsigned* line = (unsigned*) pixels; + int nn; + + for (nn = 0; nn < w; nn++) { + unsigned c = line[nn]; + unsigned ag = (c >> 8) & 0x00ff00ff; + unsigned rb = (c) & 0x00ff00ff; + + ag = (ag*alpha) & 0xff00ff00; + rb = ((rb*alpha) >> 8) & 0x00ff00ff; + + line[nn] = (unsigned)(ag | rb); + } + pixels += pitch; + } + } + else if (alpha > LCD_BRIGHTNESS_HIGH) /* 'superluminous' mode */ + { + const unsigned alpha_max = (255*LCD_ALPHA_HIGH_MAX); + const unsigned alpha_range = (255-alpha_max); + unsigned ialpha; + + alpha = ((alpha - b_high)*alpha_range) / (b_max - b_high); + ialpha = 255-alpha; + + for ( ; h > 0; h-- ) { + unsigned* line = (unsigned*) pixels; + int nn; + + for (nn = 0; nn < w; nn++) { + unsigned c = line[nn]; + unsigned ag = (c >> 8) & 0x00ff00ff; + unsigned rb = (c) & 0x00ff00ff; + + /* interpolate towards bright white, i.e. 0x00ffffff */ + ag = ((ag*ialpha + 0x00ff00ff*alpha)) & 0xff00ff00; + rb = ((rb*ialpha + 0x00ff00ff*alpha) >> 8) & 0x00ff00ff; + + line[nn] = (unsigned)(ag | rb); + } + pixels += pitch; + } + } +} + + +/* this is called when the LCD framebuffer is off */ +static void +lcd_off_argb32( unsigned char* pixels, SkinRect* r, int pitch ) +{ + int x = r->pos.x; + int y = r->pos.y; + int w = r->size.w; + int h = r->size.h; + + pixels += 4*x + y*pitch; + for ( ; h > 0; h-- ) { + memset( pixels, 0, w*4 ); + pixels += pitch; + } +} + + +static void +display_redraw( ADisplay* disp, SkinRect* rect, SDL_Surface* surface ) +{ + SkinRect r; + + if (skin_rect_intersect( &r, rect, &disp->rect )) + { + int x = r.pos.x - disp->rect.pos.x; + int y = r.pos.y - disp->rect.pos.y; + int w = r.size.w; + int h = r.size.h; + int disp_w = disp->rect.size.w; + int disp_h = disp->rect.size.h; + int dst_pitch = surface->pitch; + uint8_t* dst_line = (uint8_t*)surface->pixels + r.pos.x*4 + r.pos.y*dst_pitch; + int src_pitch = disp->datasize.w*2; + uint8_t* src_line = (uint8_t*)disp->data; + int yy, xx; +#if 0 + fprintf(stderr, "--- display redraw r.pos(%d,%d) r.size(%d,%d) " + "disp.pos(%d,%d) disp.size(%d,%d) datasize(%d,%d) rect.pos(%d,%d) rect.size(%d,%d)\n", + r.pos.x - disp->rect.pos.x, r.pos.y - disp->rect.pos.y, w, h, disp->rect.pos.x, disp->rect.pos.y, + disp->rect.size.w, disp->rect.size.h, disp->datasize.w, disp->datasize.h, + rect->pos.x, rect->pos.y, rect->size.w, rect->size.h ); +#endif + SDL_LockSurface( surface ); + + if (disp->brightness == LCD_BRIGHTNESS_OFF) + { + lcd_off_argb32( surface->pixels, &r, dst_pitch ); + } + else + { + switch ( disp->rotation & 3 ) + { + case ANDROID_ROTATION_0: + src_line += x*2 + y*src_pitch; + + for (yy = h; yy > 0; yy--) + { + uint32_t* dst = (uint32_t*)dst_line; + uint16_t* src = (uint16_t*)src_line; + + for (xx = 0; xx < w; xx++) { + dst[xx] = rgb565_to_argb32(src[xx]); + } + src_line += src_pitch; + dst_line += dst_pitch; + } + break; + + case ANDROID_ROTATION_90: + src_line += y*2 + (disp_w - x - 1)*src_pitch; + + for (yy = h; yy > 0; yy--) + { + uint32_t* dst = (uint32_t*)dst_line; + uint8_t* src = src_line; + + for (xx = w; xx > 0; xx--) + { + dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]); + src -= src_pitch; + dst += 1; + } + src_line += 2; + dst_line += dst_pitch; + } + break; + + case ANDROID_ROTATION_180: + src_line += (disp_w -1 - x)*2 + (disp_h-1-y)*src_pitch; + + for (yy = h; yy > 0; yy--) + { + uint16_t* src = (uint16_t*)src_line; + uint32_t* dst = (uint32_t*)dst_line; + + for (xx = w; xx > 0; xx--) { + dst[0] = rgb565_to_argb32(src[0]); + src -= 1; + dst += 1; + } + + src_line -= src_pitch; + dst_line += dst_pitch; + } + break; + + default: /* ANDROID_ROTATION_270 */ + src_line += (disp_h-1-y)*2 + x*src_pitch; + + for (yy = h; yy > 0; yy--) + { + uint32_t* dst = (uint32_t*)dst_line; + uint8_t* src = src_line; + + for (xx = w; xx > 0; xx--) { + dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]); + dst += 1; + src += src_pitch; + } + src_line -= 2; + dst_line += dst_pitch; + } + } +#if DOT_MATRIX + dotmatrix_dither_argb32( surface->pixels, r.pos.x, r.pos.y, r.size.w, r.size.h, surface->pitch ); +#endif + /* apply lightness */ + lcd_brightness_argb32( surface->pixels, &r, surface->pitch, disp->brightness ); + } + SDL_UnlockSurface( surface ); + + /* Apply onion skin */ + if (disp->onion != NULL) { + SkinRect r2; + + if ( skin_rect_intersect( &r2, &r, &disp->onion_rect ) ) { + SDL_Rect rs, rd; + + rd.x = r2.pos.x; + rd.y = r2.pos.y; + rd.w = r2.size.w; + rd.h = r2.size.h; + + rs.x = rd.x - disp->onion_rect.pos.x; + rs.y = rd.y - disp->onion_rect.pos.y; + rs.w = rd.w; + rs.h = rd.h; + + SDL_BlitSurface( skin_image_surface(disp->onion), &rs, surface, &rd ); + } + } + + SDL_UpdateRect( surface, r.pos.x, r.pos.y, w, h ); + } +} + + +typedef struct Button { + SkinImage* image; + SkinRect rect; + SkinPos origin; + Background* background; + unsigned keycode; + int down; +} Button; + +static void +button_done( Button* button ) +{ + skin_image_unref( &button->image ); + button->background = NULL; +} + +static void +button_init( Button* button, SkinButton* sbutton, SkinLocation* loc, Background* back, SkinRect* frame ) +{ + SkinRect r; + + button->image = skin_image_rotate( sbutton->image, loc->rotation ); + button->background = back; + button->keycode = sbutton->keycode; + button->down = 0; + + skin_rect_rotate( &r, &sbutton->rect, loc->rotation ); + r.pos.x += loc->anchor.x; + r.pos.y += loc->anchor.y; + button->origin = r.pos; + skin_rect_intersect( &button->rect, &r, frame ); +} + +static void +button_redraw( Button* button, SkinRect* rect, SDL_Surface* surface ) +{ + SkinRect r; + + if (skin_rect_intersect( &r, rect, &button->rect )) + { + if ( button->down && button->image != SKIN_IMAGE_NONE ) + { + SDL_Rect rs, rd; + + rs.x = r.pos.x - button->origin.x; + rs.y = r.pos.y - button->origin.y; + rs.w = r.size.w; + rs.h = r.size.h; + + rd.x = r.pos.x; + rd.y = r.pos.y; + rd.w = r.size.w; + rd.h = r.size.h; + + if (button->image != SKIN_IMAGE_NONE) { + SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd ); + if (button->down > 1) + SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd ); + } + } + } +} + + +typedef struct { + char tracking; + char inside; + SkinPos pos; + ADisplay* display; +} FingerState; + +static void +finger_state_reset( FingerState* finger ) +{ + finger->tracking = 0; + finger->inside = 0; +} + +typedef struct { + Button* pressed; + Button* hover; +} ButtonState; + +static void +button_state_reset( ButtonState* button ) +{ + button->pressed = NULL; + button->hover = NULL; +} + +typedef struct { + char tracking; + SkinTrackBall* ball; + SkinRect rect; + SkinWindow* window; +} BallState; + +static void +ball_state_reset( BallState* state, SkinWindow* window ) +{ + state->tracking = 0; + state->ball = NULL; + + state->rect.pos.x = 0; + state->rect.pos.y = 0; + state->rect.size.w = 0; + state->rect.size.h = 0; + state->window = window; +} + +static void +ball_state_redraw( BallState* state, SkinRect* rect, SDL_Surface* surface ) +{ + SkinRect r; + + if (skin_rect_intersect( &r, rect, &state->rect )) + skin_trackball_draw( state->ball, 0, 0, surface ); +} + +static void +ball_state_show( BallState* state, int enable ) +{ + if (enable) { + if ( !state->tracking ) { + state->tracking = 1; + SDL_ShowCursor(0); + SDL_WM_GrabInput( SDL_GRAB_ON ); + skin_trackball_refresh( state->ball ); + skin_window_redraw( state->window, &state->rect ); + } + } else { + if ( state->tracking ) { + state->tracking = 0; + SDL_WM_GrabInput( SDL_GRAB_OFF ); + SDL_ShowCursor(1); + skin_window_redraw( state->window, &state->rect ); + } + } +} + + +static void +ball_state_set( BallState* state, SkinTrackBall* ball ) +{ + ball_state_show( state, 0 ); + + state->ball = ball; + if (ball != NULL) { + SDL_Rect sr; + + skin_trackball_rect( ball, &sr ); + state->rect.pos.x = sr.x; + state->rect.pos.y = sr.y; + state->rect.size.w = sr.w; + state->rect.size.h = sr.h; + } +} + +typedef struct Layout { + int num_buttons; + int num_backgrounds; + int num_displays; + unsigned color; + Button* buttons; + Background* backgrounds; + ADisplay* displays; + SkinRect rect; + SkinLayout* slayout; +} Layout; + +#define LAYOUT_LOOP_BUTTONS(layout,button) \ + do { \ + Button* __button = (layout)->buttons; \ + Button* __button_end = __button + (layout)->num_buttons; \ + for ( ; __button < __button_end; __button ++ ) { \ + Button* button = __button; + +#define LAYOUT_LOOP_END_BUTTONS \ + } \ + } while (0); + +#define LAYOUT_LOOP_DISPLAYS(layout,display) \ + do { \ + ADisplay* __display = (layout)->displays; \ + ADisplay* __display_end = __display + (layout)->num_displays; \ + for ( ; __display < __display_end; __display ++ ) { \ + ADisplay* display = __display; + +#define LAYOUT_LOOP_END_DISPLAYS \ + } \ + } while (0); + + +static void +layout_done( Layout* layout ) +{ + int nn; + + for (nn = 0; nn < layout->num_buttons; nn++) + button_done( &layout->buttons[nn] ); + + for (nn = 0; nn < layout->num_backgrounds; nn++) + background_done( &layout->backgrounds[nn] ); + + for (nn = 0; nn < layout->num_displays; nn++) + display_done( &layout->displays[nn] ); + + qemu_free( layout->buttons ); + layout->buttons = NULL; + + qemu_free( layout->backgrounds ); + layout->backgrounds = NULL; + + qemu_free( layout->displays ); + layout->displays = NULL; + + layout->num_buttons = 0; + layout->num_backgrounds = 0; + layout->num_displays = 0; +} + +static int +layout_init( Layout* layout, SkinLayout* slayout ) +{ + int n_buttons, n_backgrounds, n_displays; + + /* first, count the number of elements of each kind */ + n_buttons = 0; + n_backgrounds = 0; + n_displays = 0; + + layout->color = slayout->color; + layout->slayout = slayout; + + SKIN_LAYOUT_LOOP_LOCS(slayout,loc) + SkinPart* part = loc->part; + + if ( part->background->valid ) + n_backgrounds += 1; + if ( part->display->valid ) + n_displays += 1; + + SKIN_PART_LOOP_BUTTONS(part, sbutton) + n_buttons += 1; + sbutton=sbutton; + SKIN_PART_LOOP_END + SKIN_LAYOUT_LOOP_END + + layout->num_buttons = n_buttons; + layout->num_backgrounds = n_backgrounds; + layout->num_displays = n_displays; + + /* now allocate arrays, then populate them */ + layout->buttons = qemu_mallocz( sizeof(Button) * n_buttons ); + layout->backgrounds = qemu_mallocz( sizeof(Background) * n_backgrounds ); + layout->displays = qemu_mallocz( sizeof(ADisplay) * n_displays ); + + if (layout->buttons == NULL && n_buttons > 0) goto Fail; + if (layout->backgrounds == NULL && n_backgrounds > 0) goto Fail; + if (layout->displays == NULL && n_displays > 0) goto Fail; + + n_buttons = 0; + n_backgrounds = 0; + n_displays = 0; + + layout->rect.pos.x = 0; + layout->rect.pos.y = 0; + layout->rect.size = slayout->size; + + SKIN_LAYOUT_LOOP_LOCS(slayout,loc) + SkinPart* part = loc->part; + Background* back = NULL; + + if ( part->background->valid ) { + back = layout->backgrounds + n_backgrounds; + background_init( back, part->background, loc, &layout->rect ); + n_backgrounds += 1; + } + if ( part->display->valid ) { + ADisplay* disp = layout->displays + n_displays; + display_init( disp, part->display, loc, &layout->rect ); + n_displays += 1; + } + + SKIN_PART_LOOP_BUTTONS(part, sbutton) + Button* button = layout->buttons + n_buttons; + button_init( button, sbutton, loc, back, &layout->rect ); + n_buttons += 1; + SKIN_PART_LOOP_END + SKIN_LAYOUT_LOOP_END + + return 0; + +Fail: + layout_done(layout); + return -1; +} + +struct SkinWindow { + SDL_Surface* surface; + Layout layout; + SkinPos pos; + FingerState finger; + ButtonState button; + BallState ball; + char enabled; + char fullscreen; + char no_display; + + char enable_touch; + char enable_trackball; + char enable_dpad; + char enable_qwerty; + + SkinImage* onion; + SkinRotation onion_rotation; + int onion_alpha; + + int x_pos; + int y_pos; + + SkinScaler* scaler; + int shrink; + double shrink_scale; + unsigned* shrink_pixels; + SDL_Surface* shrink_surface; + + double effective_scale; + double effective_x; + double effective_y; +}; + +static void +add_finger_event(unsigned x, unsigned y, unsigned state) +{ + //fprintf(stderr, "::: finger %d,%d %d\n", x, y, state); + kbd_mouse_event(x, y, 0, state); +} + +static void +skin_window_find_finger( SkinWindow* window, + int x, + int y ) +{ + FingerState* finger = &window->finger; + + /* find the display that contains this movement */ + finger->display = NULL; + finger->inside = 0; + + if (!window->enable_touch) + return; + + LAYOUT_LOOP_DISPLAYS(&window->layout,disp) + if ( skin_rect_contains( &disp->rect, x, y ) ) { + finger->inside = 1; + finger->display = disp; + finger->pos.x = x - disp->origin.x; + finger->pos.y = y - disp->origin.y; + + skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation ); + break; + } + LAYOUT_LOOP_END_DISPLAYS +} + +static void +skin_window_move_mouse( SkinWindow* window, + int x, + int y ) +{ + FingerState* finger = &window->finger; + ButtonState* button = &window->button; + + if (finger->tracking) { + ADisplay* disp = finger->display; + char inside = 1; + int dx = x - disp->rect.pos.x; + int dy = y - disp->rect.pos.y; + + if (dx < 0) { + dx = 0; + inside = 0; + } + else if (dx >= disp->rect.size.w) { + dx = disp->rect.size.w - 1; + inside = 0; + } + if (dy < 0) { + dy = 0; + inside = 0; + } else if (dy >= disp->rect.size.h) { + dy = disp->rect.size.h-1; + inside = 0; + } + finger->inside = inside; + finger->pos.x = dx + (disp->rect.pos.x - disp->origin.x); + finger->pos.y = dy + (disp->rect.pos.y - disp->origin.y); + + skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation ); + } + + { + Button* hover = button->hover; + + if (hover) { + if ( skin_rect_contains( &hover->rect, x, y ) ) + return; + + hover->down = 0; + skin_window_redraw( window, &hover->rect ); + button->hover = NULL; + } + + hover = NULL; + LAYOUT_LOOP_BUTTONS( &window->layout, butt ) + if ( skin_rect_contains( &butt->rect, x, y ) ) { + hover = butt; + break; + } + LAYOUT_LOOP_END_BUTTONS + + /* filter DPAD and QWERTY buttons right here */ + if (hover != NULL) { + switch (hover->keycode) { + /* these correspond to the DPad */ + case kKeyCodeDpadUp: + case kKeyCodeDpadDown: + case kKeyCodeDpadLeft: + case kKeyCodeDpadRight: + case kKeyCodeDpadCenter: + if (!window->enable_dpad) + hover = NULL; + break; + + /* these correspond to non-qwerty buttons */ + case kKeyCodeSoftLeft: + case kKeyCodeSoftRight: + case kKeyCodeVolumeUp: + case kKeyCodeVolumeDown: + case kKeyCodePower: + case kKeyCodeHome: + case kKeyCodeBack: + case kKeyCodeCall: + case kKeyCodeEndCall: + break; + + /* all the rest is assumed to be qwerty */ + default: + if (!window->enable_qwerty) + hover = NULL; + } + } + + if (hover != NULL) { + hover->down = 1; + skin_window_redraw( window, &hover->rect ); + button->hover = hover; + } + } +} + +static void +skin_window_trackball_press( SkinWindow* window, int down ) +{ + send_key_event( kKeyCodeBtnMouse, down ); +} + +static void +skin_window_trackball_move( SkinWindow* window, int xrel, int yrel ) +{ + BallState* state = &window->ball; + + if ( skin_trackball_move( state->ball, xrel, yrel ) ) { + skin_trackball_refresh( state->ball ); + skin_window_redraw( window, &state->rect ); + } +} + +void +skin_window_set_trackball( SkinWindow* window, SkinTrackBall* ball ) +{ + BallState* state = &window->ball; + + ball_state_set( state, ball ); +} + +void +skin_window_show_trackball( SkinWindow* window, int enable ) +{ + BallState* state = &window->ball; + + if (state->ball != NULL && window->enable_trackball) { + ball_state_show(state, enable); + } +} + + +static int skin_window_reset_internal (SkinWindow*, SkinLayout*); + +SkinWindow* +skin_window_create( SkinLayout* slayout, int x, int y, double scale, int no_display ) +{ + SkinWindow* window = qemu_mallocz(sizeof(*window)); + + window->shrink_scale = scale; + window->shrink = (scale != 1.0); + window->scaler = skin_scaler_create(); + window->no_display = no_display; + + /* enable everything by default */ + window->enable_touch = 1; + window->enable_trackball = 1; + window->enable_dpad = 1; + window->enable_qwerty = 1; + + window->x_pos = x; + window->y_pos = y; + + if (skin_window_reset_internal(window, slayout) < 0) { + skin_window_free( window ); + return NULL; + } + //SDL_WM_SetCaption( "Android Emulator", "Android Emulator" ); + + SDL_WM_SetPos( x, y ); + if ( !SDL_WM_IsFullyVisible( 1 ) ) { + dprint( "emulator window was out of view and was recentred\n" ); + } + + return window; +} + +void +skin_window_enable_touch( SkinWindow* window, int enabled ) +{ + window->enable_touch = !!enabled; +} + +void +skin_window_enable_trackball( SkinWindow* window, int enabled ) +{ + window->enable_trackball = !!enabled; +} + +void +skin_window_enable_dpad( SkinWindow* window, int enabled ) +{ + window->enable_dpad = !!enabled; +} + +void +skin_window_enable_qwerty( SkinWindow* window, int enabled ) +{ + window->enable_qwerty = !!enabled; +} + +void +skin_window_set_title( SkinWindow* window, const char* title ) +{ + if (window && title) + SDL_WM_SetCaption( title, title ); +} + +static void +skin_window_resize( SkinWindow* window ) +{ + /* now resize window */ + if (window->surface) { + SDL_FreeSurface(window->surface); + window->surface = NULL; + } + + if (window->shrink_surface) { + SDL_FreeSurface(window->shrink_surface); + window->shrink_surface = NULL; + } + + if (window->shrink_pixels) { + qemu_free(window->shrink_pixels); + window->shrink_pixels = NULL; + } + + if ( !window->no_display ) { + int layout_w = window->layout.rect.size.w; + int layout_h = window->layout.rect.size.h; + int window_w = layout_w; + int window_h = layout_h; + int window_x = window->x_pos; + int window_y = window->y_pos; + int flags; + SDL_Surface* surface; + double scale = 1.0; + int fullscreen = window->fullscreen; + + if (fullscreen) { + if (get_nearest_monitor_rect(&window_x, &window_y, + &window_w, &window_h) < 0) { + fullscreen = 0; + } else { + double x_scale = window_w * 1.0 / layout_w; + double y_scale = window_h * 1.0 / layout_h; + + scale = (x_scale <= y_scale) ? x_scale : y_scale; + } + } + else if (window->shrink) { + scale = window->shrink_scale; + window_w = (int) ceil(layout_w*scale); + window_h = (int) ceil(layout_h*scale); + } + + { + char temp[32]; + sprintf(temp,"SDL_VIDEO_WINDOW_POS=%d,%d",window_x,window_y); + putenv(temp); + putenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE=1"); + } + + flags = SDL_SWSURFACE; + if (fullscreen) { + flags |= SDL_FULLSCREEN; + } + surface = SDL_SetVideoMode( window_w, window_h, 32, flags ); + if (surface == NULL) { + fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() ); + exit(1); + } + + SDL_WM_SetPos( window_x, window_y ); + + window->effective_scale = scale; + window->effective_x = 0; + window->effective_y = 0; + + if (fullscreen) { + window->effective_x = (window_w - layout_w*scale)*0.5; + window->effective_y = (window_h - layout_h*scale)*0.5; + } + + if (scale == 1.0) + window->surface = surface; + else + { + window_w = (int) ceil(window_w / scale ); + window_h = (int) ceil(window_h / scale ); + + window->shrink_surface = surface; + window->shrink_pixels = qemu_mallocz( window_w * window_h * 4 ); + if (window->shrink_pixels == NULL) { + fprintf(stderr, "### Error: could not allocate memory for rescaling surface\n"); + exit(1); + } + window->surface = sdl_surface_from_argb32( window->shrink_pixels, window_w, window_h ); + if (window->surface == NULL) { + fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() ); + exit(1); + } + skin_scaler_set( window->scaler, scale, window->effective_x, window->effective_y ); + } + } +} + +static int +skin_window_reset_internal ( SkinWindow* window, SkinLayout* slayout ) +{ + Layout layout; + ADisplay* disp; + + if ( layout_init( &layout, slayout ) < 0 ) + return -1; + + disp = window->layout.displays; + + layout_done( &window->layout ); + window->layout = layout; + + disp = window->layout.displays; + if (disp != NULL && window->onion) + display_set_onion( disp, + window->onion, + window->onion_rotation, + window->onion_alpha ); + + skin_window_resize(window); + + finger_state_reset( &window->finger ); + button_state_reset( &window->button ); + ball_state_reset( &window->ball, window ); + + skin_window_redraw( window, NULL ); + + if (slayout->event_type != 0) { + kbd_generic_event( slayout->event_type, slayout->event_code, slayout->event_value ); + } + + return 0; +} + +int +skin_window_reset ( SkinWindow* window, SkinLayout* slayout ) +{ + if (!window->fullscreen) { + SDL_WM_GetPos(&window->x_pos, &window->y_pos); + } + return skin_window_reset_internal( window, slayout ); +} + +void +skin_window_set_lcd_brightness( SkinWindow* window, int brightness ) +{ + ADisplay* disp = window->layout.displays; + + if (disp != NULL) { + disp->brightness = brightness; + skin_window_redraw( window, NULL ); + } +} + +void +skin_window_free ( SkinWindow* window ) +{ + if (window) { + if (window->surface) { + SDL_FreeSurface(window->surface); + window->surface = NULL; + } + if (window->shrink_surface) { + SDL_FreeSurface(window->shrink_surface); + window->shrink_surface = NULL; + } + if (window->shrink_pixels) { + qemu_free(window->shrink_pixels); + window->shrink_pixels = NULL; + } + if (window->onion) { + skin_image_unref( &window->onion ); + window->onion_rotation = SKIN_ROTATION_0; + } + if (window->scaler) { + skin_scaler_free(window->scaler); + window->scaler = NULL; + } + layout_done( &window->layout ); + qemu_free(window); + } +} + +void +skin_window_set_onion( SkinWindow* window, + SkinImage* onion, + SkinRotation onion_rotation, + int onion_alpha ) +{ + ADisplay* disp; + SkinImage* old = window->onion; + + window->onion = skin_image_ref(onion); + window->onion_rotation = onion_rotation; + window->onion_alpha = onion_alpha; + + skin_image_unref( &old ); + + disp = window->layout.displays; + + if (disp != NULL) + display_set_onion( disp, window->onion, onion_rotation, onion_alpha ); +} + +static void +skin_window_update_shrink( SkinWindow* window, SkinRect* rect ) +{ + skin_scaler_scale( window->scaler, window->shrink_surface, window->surface, + rect->pos.x, rect->pos.y, rect->size.w, rect->size.h ); +} + +void +skin_window_set_scale( SkinWindow* window, double scale ) +{ + window->shrink = (scale != 1.0); + window->shrink_scale = scale; + + skin_window_resize( window ); + skin_window_redraw( window, NULL ); +} + +void +skin_window_redraw( SkinWindow* window, SkinRect* rect ) +{ + if (window != NULL && window->surface != NULL) { + Layout* layout = &window->layout; + + if (rect == NULL) + rect = &layout->rect; + + { + SkinRect r; + + if ( skin_rect_intersect( &r, rect, &layout->rect ) ) { + SDL_Rect rd; + rd.x = r.pos.x; + rd.y = r.pos.y; + rd.w = r.size.w; + rd.h = r.size.h; + + SDL_FillRect( window->surface, &rd, layout->color ); + } + } + + { + Background* back = layout->backgrounds; + Background* end = back + layout->num_backgrounds; + for ( ; back < end; back++ ) + background_redraw( back, rect, window->surface ); + } + + { + ADisplay* disp = layout->displays; + ADisplay* end = disp + layout->num_displays; + for ( ; disp < end; disp++ ) + display_redraw( disp, rect, window->surface ); + } + + { + Button* button = layout->buttons; + Button* end = button + layout->num_buttons; + for ( ; button < end; button++ ) + button_redraw( button, rect, window->surface ); + } + + if ( window->ball.tracking ) + ball_state_redraw( &window->ball, rect, window->surface ); + + if (window->effective_scale != 1.0) + skin_window_update_shrink( window, rect ); + else + { + SDL_Rect rd; + rd.x = rect->pos.x; + rd.y = rect->pos.y; + rd.w = rect->size.w; + rd.h = rect->size.h; + + SDL_UpdateRects( window->surface, 1, &rd ); + } + } +} + +void +skin_window_toggle_fullscreen( SkinWindow* window ) +{ + if (window && window->surface) { + if (!window->fullscreen) + SDL_WM_GetPos( &window->x_pos, &window->y_pos ); + + window->fullscreen = !window->fullscreen; + skin_window_resize( window ); + skin_window_redraw( window, NULL ); + } +} + +void +skin_window_get_display( SkinWindow* window, ADisplayInfo *info ) +{ + ADisplay* disp = window->layout.displays; + + if (disp != NULL) { + info->width = disp->datasize.w; + info->height = disp->datasize.h; + info->rotation = disp->rotation; + info->data = disp->data; + } else { + info->width = 0; + info->height = 0; + info->rotation = SKIN_ROTATION_0; + info->data = NULL; + } +} + + +static void +skin_window_map_to_scale( SkinWindow* window, int *x, int *y ) +{ + *x = (*x - window->effective_x) / window->effective_scale; + *y = (*y - window->effective_y) / window->effective_scale; +} + +void +skin_window_process_event( SkinWindow* window, SDL_Event* ev ) +{ + Button* button; + int mx, my; + + if (!window->surface) + return; + + switch (ev->type) { + case SDL_MOUSEBUTTONDOWN: + if ( window->ball.tracking ) { + skin_window_trackball_press( window, 1 ); + break; + } + + mx = ev->button.x; + my = ev->button.y; + skin_window_map_to_scale( window, &mx, &my ); + skin_window_move_mouse( window, mx, my ); + skin_window_find_finger( window, mx, my ); +#if 0 + printf("down: x=%d y=%d fx=%d fy=%d fis=%d\n", + ev->button.x, ev->button.y, window->finger.pos.x, + window->finger.pos.y, window->finger.inside); +#endif + if (window->finger.inside) { + window->finger.tracking = 1; + add_finger_event(window->finger.pos.x, window->finger.pos.y, 1); + } else { + window->button.pressed = NULL; + button = window->button.hover; + if(button) { + button->down += 1; + skin_window_redraw( window, &button->rect ); + window->button.pressed = button; + if(button->keycode) { + send_key_event(button->keycode, 1); + } + } + } + break; + + case SDL_MOUSEBUTTONUP: + if ( window->ball.tracking ) { + skin_window_trackball_press( window, 0 ); + break; + } + button = window->button.pressed; + mx = ev->button.x; + my = ev->button.y; + skin_window_map_to_scale( window, &mx, &my ); + if (button) + { + button->down = 0; + skin_window_redraw( window, &button->rect ); + if(button->keycode) { + send_key_event(button->keycode, 0); + } + window->button.pressed = NULL; + window->button.hover = NULL; + skin_window_move_mouse( window, mx, my ); + } + else if (window->finger.tracking) + { + skin_window_move_mouse( window, mx, my ); + window->finger.tracking = 0; + add_finger_event( window->finger.pos.x, window->finger.pos.y, 0); + } + break; + + case SDL_MOUSEMOTION: + if ( window->ball.tracking ) { + skin_window_trackball_move( window, ev->motion.xrel, ev->motion.yrel ); + break; + } + mx = ev->button.x; + my = ev->button.y; + skin_window_map_to_scale( window, &mx, &my ); + if ( !window->button.pressed ) + { + skin_window_move_mouse( window, mx, my ); + if ( window->finger.tracking ) { + add_finger_event( window->finger.pos.x, window->finger.pos.y, 1 ); + } + } + break; + } +} + +static ADisplay* +skin_window_display( SkinWindow* window ) +{ + return window->layout.displays; +} + +void +skin_window_update_display( SkinWindow* window, int x, int y, int w, int h ) +{ + ADisplay* disp = skin_window_display(window); + + if ( !window->surface ) + return; + + if (disp != NULL) { + SkinRect r; + r.pos.x = x; + r.pos.y = y; + r.size.w = w; + r.size.h = h; + + skin_rect_rotate( &r, &r, disp->rotation ); + r.pos.x += disp->origin.x; + r.pos.y += disp->origin.y; + + if (window->effective_scale != 1.0) + skin_window_redraw( window, &r ); + else + display_redraw( disp, &r, window->surface ); + } +} |