diff --git a/Makefile.target b/Makefile.target index 2f57713c5..21978628c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -357,8 +357,15 @@ obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += s5l8900.o iphone2g.o pcf50633.o s5l8900_uart.o s5l8900_spi.o pl192.o -obj-arm-y += s5l8900_i2c.o usb_synopsys.o #s5l8900_usb_otg.o - +obj-arm-y += s5l8900_i2c.o usb_synopsys.o + +#ifdef CONFIG_SKINNING +VPATH+=:$(SRC_PATH)/skin +CFLAGS+=-I$(SRC_PATH)/skin $(PNG_CFLAGS) +LIBS+=$(PNG_LIBS) -lexpat +obj-arm-y += skinning.o skin_config.o skin_image.o skin_button.o +obj-i386-y += skinning.o skin_config.o skin_image.o skin_button.o +#endif # CONFIG_SKINNING obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/README.txt b/README.txt index f53c1f075..8fa0d0d26 100644 --- a/README.txt +++ b/README.txt @@ -5,7 +5,7 @@ make How to run: -./arm-softmmu/qemu-system-arm -M iphone2g -option-rom iBoot-1.0.2.m68ap.RELEASE -option-rom iphone1-bootrom.bin -pflash nordump.bin -serial stdio +./arm-softmmu/qemu-system-arm -M iphone2g -option-rom iBoot-1.0.2.m68ap.RELEASE -option-rom iphone1-bootrom.bin -pflash nordump.bin -serial stdio -skin ./skin/devices/iphone2g/skin.xml How do i contribute: diff --git a/configure b/configure index faaed6054..5a1549229 100755 --- a/configure +++ b/configure @@ -174,6 +174,7 @@ zero_malloc="" trace_backend="nop" trace_file="trace" spice="" +skinning="no" rbd="" smartcard="" smartcard_nss="" @@ -724,6 +725,8 @@ for opt do ;; --enable-opengl) opengl="yes" ;; + --enable-skinning) skinning="yes" + ;; --*dir) ;; --disable-rbd) rbd="no" @@ -928,6 +931,7 @@ echo " --enable-docs enable documentation build" echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" +echo " --enable-skinning Enabled skinning functionality for Qemu" echo " --enable-trace-backend=B Set trace backend" echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends) echo " --with-trace-file=NAME Full PATH,NAME of file to store traces" @@ -2879,6 +2883,9 @@ fi if test "$fdatasync" = "yes" ; then echo "CONFIG_FDATASYNC=y" >> $config_host_mak fi +if test "$skinning" = "yes" ; then + echo "CONFIG_SKINNING=y" >>$config_host_mak +fi if test "$madvise" = "yes" ; then echo "CONFIG_MADVISE=y" >> $config_host_mak fi diff --git a/console.c b/console.c index 871c1d47b..0570c498a 100644 --- a/console.c +++ b/console.c @@ -32,6 +32,21 @@ #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff) +#ifdef CONFIG_SKINNING +// Skinning overwrites these functions to put the skin in between +DisplayState *qemu_graphic_console_init(vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + vga_hw_text_update_ptr text_update, + void *opaque); +#undef graphic_console_init +#define graphic_console_init qemu_graphic_console_init + +void original_qemu_console_resize(DisplayState *ds, int width, int height); +#undef qemu_console_resize +#define qemu_console_resize original_qemu_console_resize +#endif + typedef struct TextAttributes { uint8_t fgcol:4; uint8_t bgcol:4; @@ -227,7 +242,7 @@ static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) return color; } -static void vga_fill_rect (DisplayState *ds, +void vga_fill_rect (DisplayState *ds, int posx, int posy, int width, int height, uint32_t color) { uint8_t *d, *d1; diff --git a/console.h b/console.h index 64d1f090e..4982b62f1 100644 --- a/console.h +++ b/console.h @@ -163,7 +163,10 @@ struct DisplayChangeListener { void (*dpy_fill)(struct DisplayState *s, int x, int y, int w, int h, uint32_t c); void (*dpy_text_cursor)(struct DisplayState *s, int x, int y); - +#ifdef CONFIG_SKINNING + void (*dpy_enablezoom)(struct DisplayState *s, int width, int height); + void (*dpy_getresolution)(int *width, int *height); +#endif struct DisplayChangeListener *next; }; @@ -298,6 +301,26 @@ static inline void dpy_cursor(struct DisplayState *s, int x, int y) { } } +#ifdef CONFIG_SKINNING +static inline void dpy_enablezoom(struct DisplayState *s, int width, int height) +{ + struct DisplayChangeListener *dcl = s->listeners; + while (dcl != NULL) { + if (dcl->dpy_enablezoom) dcl->dpy_enablezoom(s, width, height); + dcl = dcl->next; + } +} + +static inline void dpy_getresolution(struct DisplayState *s, int *width, int *height) +{ + struct DisplayChangeListener *dcl = s->listeners; + while (dcl != NULL) { + if (dcl->dpy_getresolution) dcl->dpy_getresolution(width, height); + dcl = dcl->next; + } +} +#endif + static inline int ds_get_linesize(DisplayState *ds) { return ds->surface->linesize; @@ -362,6 +385,9 @@ void qemu_console_resize(DisplayState *ds, int width, int height); void qemu_console_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h); +void vga_fill_rect (DisplayState *ds, + int posx, int posy, int width, int height, uint32_t color); + /* sdl.c */ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); diff --git a/hw/iphone2g.c b/hw/iphone2g.c index 1902d077c..ae34e637e 100644 --- a/hw/iphone2g.c +++ b/hw/iphone2g.c @@ -100,7 +100,7 @@ static void iphone2g_lcd_update_display(void *opaque) (void)draw_line_table12; // Unused var. - if (!lcd || !ds_get_bits_per_pixel(lcd->ds)) + if (!lcd || !lcd->ds || !ds_get_bits_per_pixel(lcd->ds) || !frame_base) return; switch (ds_get_bits_per_pixel(lcd->ds)) { @@ -236,8 +236,6 @@ static iphone2g_lcd_s * iphone2g_lcd_init(target_phys_addr_t base) lcd->ds = graphic_console_init(iphone2g_lcd_update_display, iphone2g_lcd_invalidate_display, iphone2g_lcd_screen_dump, NULL, lcd); - qemu_console_resize(lcd->ds, LCD_WIDTH, LCD_HEIGHT); - return lcd; } diff --git a/input.c b/input.c index ec05548f7..d1ba66176 100644 --- a/input.c +++ b/input.c @@ -28,6 +28,14 @@ #include "console.h" #include "qjson.h" +#ifdef CONFIG_SKINNING +QEMUPutMouseEntry *original_qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name); +#undef qemu_add_mouse_event_handler +#define qemu_add_mouse_event_handler original_qemu_add_mouse_event_handler +#endif + static QEMUPutKBDEvent *qemu_put_kbd_event; static void *qemu_put_kbd_event_opaque; static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers); diff --git a/qemu-options.hx b/qemu-options.hx index ef60730e4..76cb4b41f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -769,6 +769,16 @@ STEXI Rotate graphical output 90 deg left (only PXA LCD). ETEXI +DEF("landscape", 0, QEMU_OPTION_landscape, + "-landscape rotate graphical output 90 deg right\n", + QEMU_ARCH_ALL) +STEXI +@item -portrait +@findex -portrait +Rotate graphical output 90 deg right +ETEXI + + DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|none]\n" " select video card type\n", QEMU_ARCH_ALL) @@ -808,6 +818,24 @@ STEXI Start in full screen. ETEXI +#if defined(CONFIG_SKINNING) +DEF("skin", HAS_ARG, QEMU_OPTION_skin, + "-skin file Skin qemu using provided skin configuration file\n", QEMU_ARCH_ALL) +STEXI +@item -skin @var{file} +Skin qemu using definitions from @var{file} +ETEXI +#endif + +#if defined(CONFIG_SKINNING) +DEF("rctport", HAS_ARG, QEMU_OPTION_rctport, + "-rctport port Allow remote control of the skin through specified port\n", QEMU_ARCH_ALL) +STEXI +@item -rctport @var{d} +Allow remote control of the skin through port @var{d} +ETEXI +#endif + DEF("g", 1, QEMU_OPTION_g , "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n", QEMU_ARCH_PPC | QEMU_ARCH_SPARC) diff --git a/skin/devices/iphone2g/iphone2g-landscape.png b/skin/devices/iphone2g/iphone2g-landscape.png new file mode 100644 index 000000000..38cff832d Binary files /dev/null and b/skin/devices/iphone2g/iphone2g-landscape.png differ diff --git a/skin/devices/iphone2g/iphone2g-portrait.png b/skin/devices/iphone2g/iphone2g-portrait.png new file mode 100644 index 000000000..0973ae5d2 Binary files /dev/null and b/skin/devices/iphone2g/iphone2g-portrait.png differ diff --git a/skin/devices/iphone2g/skin.xml b/skin/devices/iphone2g/skin.xml new file mode 100644 index 000000000..c7be880c4 --- /dev/null +++ b/skin/devices/iphone2g/skin.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/skin/skin.h b/skin/skin.h new file mode 100644 index 000000000..70c284d80 --- /dev/null +++ b/skin/skin.h @@ -0,0 +1,197 @@ +/* + * Qemu skinning header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef SKIN_H__ +#define SKIN_H__ + +typedef enum SwitchDefaultState { + undefined = -1, + off = 0, + on +} SwitchDefaultState; + +typedef struct SkinImageConfig { + char* file; + int posx; + int posy; + struct SkinImageConfig* next; +} SkinImageConfig; + +typedef struct SkinBackgroundColor { + int red; + int green; + int blue; +} SkinBackgroundColor; + +typedef struct SkinButtonConfig { + SkinImageConfig image; + unsigned int keycode; + unsigned int isswitch; + SwitchDefaultState defaultstate; + char* tooltip; + struct SkinButtonConfig* next; +} SkinButtonConfig; + +typedef struct SkinKeyConfig { + int posx; + int posy; + int width; + int height; + unsigned int keycode; + struct SkinKeyConfig* next; +} SkinKeyConfig; + +typedef struct SkinKeyboardConfig { + SkinImageConfig* image; + int animated; + SkinKeyConfig* keys; + int switchcode; + int screenwidth; + int screenheight; + int highlight_red; + int highlight_green; + int highlight_blue; + int offset; // Offset to move the skin +} SkinKeyboardConfig; + +typedef struct SkinLayout { + int width; + int height; + int emuscreen_posx; + int emuscreen_posy; + int emuscreen_width; + int emuscreen_height; + struct SkinBackgroundColor bgcolor; + struct SkinImageConfig* background; + struct SkinKeyboardConfig* keyboard; + struct SkinButtonConfig* buttons; +} SkinLayout; + +typedef struct SkinConfig { + struct SkinLayout* landscape; + struct SkinLayout* portrait; +} SkinConfig; + +typedef struct SkinImage { + uint16_t posx; // X Position on the skin display + uint16_t posy; // Y Position on the skin display + uint16_t width; // Width of the image + uint16_t height; // Height of the image + void* data; // Image data, encoded in PixelFormat + int linesize; // Bytes per line + struct PixelFormat pf; // Color/Bit information +} SkinImage; + +typedef enum SkinButtonState { + ESkinBtn_Idle = 0, + ESkinBtn_Highlighted, + ESkinBtn_Active, + ESkinBtn_ActiveHighlighted, + // Button state count + ESkinBtn_statecount, + ESkinBtn_forceredraw +} SkinButtonState; + +typedef struct SkinKeyEvent { + uint8_t mouseover : 1; // Mouse is hovered over the button + uint8_t mousedown : 1; // Mouse LSK is clicked on the button currently + uint8_t keydown : 1; // The related key is pressed currently +} SkinKeyEvent; + +typedef struct SkinKey { // Has same structure as SkinButton with + uint8_t keycode; // posx - height from SkinImage + SkinKeyEvent event; + uint8_t isswitch : 1; + int8_t defaultstate : 3; + uint8_t state : 4; + uint16_t posx; + uint16_t posy; + uint16_t width; + uint16_t height; + struct SkinKey* next; +} SkinKey; + +typedef struct SkinButton { // SkinButton and SkinKey share information + struct SkinImage image; + struct SkinKey key; + char* tooltip; + struct SkinButton* next; +} SkinButton; + +typedef struct EmulatedScreen { + struct DisplayState* ds; // DisplayState used by client display drivers + int posx; // X Position on the skin display + int posy; // Y Position on the skin display + int width; // Required width of this display + int height; // Required height of this display +} EmulatedScreen; + +typedef struct SkinKeyboard { + SkinImage* image; + int animated; + SkinKey* keys; + SkinButton* button; + int screenwidth; + int screenheight; + int highlight_red; + int highlight_green; + int highlight_blue; + int offset; +} SkinKeyboard; + +typedef struct SkinFont { + struct SkinImage* image; // Font image with characters + uint8_t char_width; // Character Width in px + uint8_t char_height; // Character Height in px +} SkinFont; + +typedef struct SkinTooltip { + uint32_t color; // color for the tooltip background + QEMUTimer *timer; // Timeout before the tooltip is shown + SkinButton *button; // Button that activated the tooltip + SkinImage *image; // Cached image for redrawing +} SkinTooltip; + +typedef struct SkinScreen { + int width; // Total width of the display + int height; // Total height of the display + struct SkinBackgroundColor bgcolor; + struct DisplayState* ds; // DisplayState towards qemu + struct EmulatedScreen* es; // Emulated display information + struct SkinImage* background; + struct SkinKeyboard keyboard; + struct SkinButton* buttons; + struct SkinFont* font; + struct SkinTooltip tooltip; + int rotation; + int rotation_req; + int mouse_event; + struct SkinConfig* config; // Possible skin configurations to use +} SkinScreen; + +typedef struct SkinArea { + int x; + int y; + int width; + int height; +} SkinArea; + +#endif /* SKIN_H__ */ + diff --git a/skin/skin_button.c b/skin/skin_button.c new file mode 100644 index 000000000..01bc26896 --- /dev/null +++ b/skin/skin_button.c @@ -0,0 +1,250 @@ +/* + * Skin button handling + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include "console.h" + +#include "skin_button.h" +#include "skin_switchstate.h" + +typedef struct SkinButtonStateCallback { + switchstate_callback *callback; + void *opaque; + // Next callback + struct SkinButtonStateCallback *next; +} SkinButtonStateCallback; + +static SkinButtonStateCallback *switchcb = NULL; + +int skin_button_handle_mouse(SkinKey* button, int mousebtn) +{ + button->event.mouseover = 1; + if(button->isswitch) { + int newstate = button->state; + if(!mousebtn && button->event.mousedown) { + // Trigger the switch + if(newstate == ESkinBtn_Highlighted) + newstate = ESkinBtn_ActiveHighlighted; + else + newstate = ESkinBtn_Highlighted; + if (button->keycode & 0x80) { + kbd_put_keycode(0xe0); + kbd_put_keycode(button->keycode); + } + else { + kbd_put_keycode(button->keycode | 0x80); + } + } + + if(newstate == ESkinBtn_Idle) newstate = ESkinBtn_Highlighted; + if(newstate == ESkinBtn_Active) newstate = ESkinBtn_ActiveHighlighted; + + button->event.mousedown = mousebtn != 0; + return newstate; + } else { + if(mousebtn && !button->event.mousedown) { + if (button->keycode & 0x80) { + kbd_put_keycode(0xe0); + kbd_put_keycode(button->keycode & 0x7F); + } + else { + kbd_put_keycode(button->keycode); + } + } + if(!mousebtn && button->event.mousedown) { + if (button->keycode & 0x80) { + kbd_put_keycode(0xe0); + kbd_put_keycode(button->keycode); + } + else { + kbd_put_keycode(button->keycode | 0x80); + } + } + button->event.mousedown = mousebtn != 0; + return ESkinBtn_Highlighted; + } +} + +int skin_button_handle_mouseleave(SkinKey* button) +{ + if(button->isswitch) { + int newstate = button->event.keydown == 0 ? ESkinBtn_Idle : ESkinBtn_Highlighted; + button->event.mousedown = 0; + button->event.mouseover = 0; + if(button->state == ESkinBtn_Active || + button->state == ESkinBtn_ActiveHighlighted ) { + if(!button->event.keydown) newstate = ESkinBtn_Active; + else newstate = ESkinBtn_ActiveHighlighted; + } + return newstate; + } else { + if(button->event.mousedown) { + if (button->keycode & 0x80) { + kbd_put_keycode(0xe0); + kbd_put_keycode(button->keycode); + } + else { + kbd_put_keycode(button->keycode | 0x80); + } + } + button->event.mousedown = 0; + button->event.mouseover = 0; + if(button->event.keydown) return ESkinBtn_Highlighted; + return ESkinBtn_Idle; + } +} + +int skin_button_handle_key(SkinKey* button, int keypressed) +{ + int newstate = button->state; + if(keypressed) { + button->event.keydown = 1; + switch(button->state) { + case ESkinBtn_Idle: + newstate = ESkinBtn_Highlighted; + break; + case ESkinBtn_Active: + newstate = ESkinBtn_ActiveHighlighted; + break; + case ESkinBtn_Highlighted: + case ESkinBtn_ActiveHighlighted: + break; + default: + fprintf(stderr, "skin_key_handler, wrong state for switch having key: %d\n", button->keycode); + break; + } + } else { + button->event.keydown = 0; + switch(button->state) { + case ESkinBtn_Idle: + case ESkinBtn_Active: + break; + case ESkinBtn_Highlighted: + if(!button->event.mouseover) newstate = ESkinBtn_Idle; + break; + case ESkinBtn_ActiveHighlighted: + if(!button->event.mouseover) newstate = ESkinBtn_Active; + break; + default: + fprintf(stderr, "skin_key_handler, wrong state for switch having key: %d\n", button->keycode); + break; + } + } + + if(!keypressed && button->isswitch) { + switch(newstate) { + case ESkinBtn_Idle: + case ESkinBtn_Highlighted: + if(!button->event.mouseover) newstate = ESkinBtn_Active; + else newstate = ESkinBtn_ActiveHighlighted; + break; + case ESkinBtn_Active: + case ESkinBtn_ActiveHighlighted: + if(!button->event.mouseover) newstate = ESkinBtn_Idle; + else newstate = ESkinBtn_Highlighted; + break; + default: + fprintf(stderr, "skin_key_handler, wrong state for switch having key: %d\n", button->keycode); + break; + } + } + return newstate; +} + +void skin_button_checkswitch(SkinScreen* skin, SkinButton* button) +{ + //printf(">> skin_button_checkswitch\n"); + if (button->key.isswitch) { + int hwstate = unknown; + SkinButtonStateCallback *cb = switchcb; + while (cb && hwstate == unknown) { + hwstate = cb->callback(cb->opaque, button->key.keycode); + if (cb->next) cb = cb->next; + else break; + } + + if (button->key.defaultstate == on) { + // If default switch state is on, it overrides the hardware state. + // Make sure the hardware state is in sync also. + //printf("skin_button_checkswitch, button=%d, defaultstate=on\n", button->key.keycode); + if (hwstate == inactive) { + //printf("hwstate=inactive, defaultstate alreadyset for button %d\n", button->key.keycode); + button->key.defaultstate = undefined; + kbd_put_keycode(button->key.keycode | 0x80); + hwstate = cb->callback(cb->opaque, button->key.keycode); + //printf("defaultstate=on, hwstate=%d\n", hwstate); + } + } + + if (button->key.defaultstate == off) { + // If default switch state is off, it overrides the hardware state. + // Make sure the hardware state is in sync also. + //printf("skin_button_checkswitch, button=%d, defaultstate=off\n", button->key.keycode); + if (hwstate == active) { + //printf("hwstate=active, defaultstate alreadyset for button %d\n", button->key.keycode); + button->key.defaultstate = undefined; + kbd_put_keycode(button->key.keycode | 0x80); + hwstate = cb->callback(cb->opaque, button->key.keycode); + //printf("defaultstate=%d, hwstate=%d for button %d\n", button->key.defaultstate, hwstate, button->key.keycode); + } + } + + // If we retrieved a valid state, update the button accordingly + if (hwstate == inactive) { + if (button->key.state == ESkinBtn_Active) button->key.state = ESkinBtn_Idle; + if (button->key.state == ESkinBtn_ActiveHighlighted) button->key.state = ESkinBtn_Highlighted; + } + if (hwstate == active) { + if (button->key.state == ESkinBtn_Idle) button->key.state = ESkinBtn_Active; + if (button->key.state == ESkinBtn_Highlighted) button->key.state = ESkinBtn_ActiveHighlighted; + } + + // If the hardware state is not yet retrieved, go with the defaultstate + if (hwstate == unknown && button->key.defaultstate == off) { + button->key.state = ESkinBtn_Idle; + } + //printf("keycode: %d, hwstate: %d\n", button->key.keycode, hwstate); + } + //printf("skin_button_checkswitch >>\n"); +} + +void qemu_skin_add_switchstate_callback(switchstate_callback *callback, + void *opaque) +{ + SkinButtonStateCallback *cb = switchcb; + if (!switchcb) { + switchcb = (SkinButtonStateCallback*)qemu_mallocz(sizeof(SkinButtonStateCallback)); + cb = switchcb; + } + else { + while (cb) cb = cb->next; + cb = (SkinButtonStateCallback*)qemu_mallocz(sizeof(SkinButtonStateCallback)); + } + + if (cb) { + // Store the parameters for later use + cb->callback = callback; + cb->opaque = opaque; + + /* if (cb->callback(opaque, 59) == active) { + printf("qemu_skin_add_switchstate_callback() registered keyboard: %d\n", cb->callback(opaque, 59)); + }*/ + } +} + diff --git a/skin/skin_button.h b/skin/skin_button.h new file mode 100644 index 000000000..42625b1cc --- /dev/null +++ b/skin/skin_button.h @@ -0,0 +1,39 @@ +/* + * Skin button handling header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "skin.h" + +#ifndef SKIN_BUTTON_H__ +#define SKIN_BUTTON_H__ + +static inline int skin_button_mouse_over(SkinKey* button, int x, int y ) { + return (x > button->posx && + y > button->posy && + x < button->posx+button->width && + y < button->posy+button->height); +} + +int skin_button_handle_mouse(SkinKey* button, int mousebtn); +int skin_button_handle_mouseleave(SkinKey* button); +int skin_button_handle_key(SkinKey* button, int keypressed); + +void skin_button_checkswitch(SkinScreen* skin, SkinButton* button); + +#endif /* SKIN_BUTTON_H__ */ diff --git a/skin/skin_config.c b/skin/skin_config.c new file mode 100644 index 000000000..5e940ced0 --- /dev/null +++ b/skin/skin_config.c @@ -0,0 +1,636 @@ +/* + * Skin configuration reading + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include "expat.h" +#include "console.h" + +#include "skin_config.h" +#include "skin_image.h" + +const int BUFFER_SIZE = 512; + +typedef struct SkinParser { + SkinScreen* skin; + SkinLayout* layout; + char* filepath; +} SkinParser; + +static inline int value_is(const char *attr, const char *name) +{ + return (strcmp(attr, name) == 0); +} + +static int skin_load_images(SkinScreen* skin, SkinLayout* layout) +{ + //printf("skin_config.c: skin_load_images, background file='%s'\n", + // layout->background->file); + if (layout->background && layout->background->file != NULL) { + skin->background = skin_load_image((char*)layout->background->file); + skin->background->posx = layout->background->posx; + skin->background->posy = layout->background->posy; + } + + if (layout->keyboard && layout->keyboard->image->file != NULL) { + skin->keyboard.image = skin_load_image((char*)layout->keyboard->image->file); + skin->keyboard.image->posx = layout->keyboard->image->posx; + skin->keyboard.image->posy = layout->keyboard->image->posy; + if (layout->keyboard->animated) { + // If keyboard image is animated, it consists of 5 pictures + skin->keyboard.image->height /= 5; + skin->keyboard.animated = 1; + } + else skin->keyboard.animated = 0; + + skin->keyboard.screenwidth = layout->keyboard->screenwidth; + skin->keyboard.screenheight = layout->keyboard->screenheight; + skin->keyboard.highlight_red = layout->keyboard->highlight_red; + skin->keyboard.highlight_green = layout->keyboard->highlight_green; + skin->keyboard.highlight_blue = layout->keyboard->highlight_blue; + skin->keyboard.offset = layout->keyboard->offset; + } + + SkinButtonConfig *latestButton = layout->buttons; + if(latestButton) { + skin->buttons = (SkinButton*)qemu_mallocz(sizeof(SkinButton)); + } + + SkinButton* button = skin->buttons; + while (latestButton) { + if (latestButton->image.file) { + //printf("load button: %s, posx=%d, posy=%d\n",latestButton->image.file, + // latestButton->image.posx, latestButton->image.posy); + skin_load_image_data(&button->image, latestButton->image.file); + button->key.posx = button->image.posx = latestButton->image.posx; + button->key.posy = button->image.posy = latestButton->image.posy; + button->key.keycode = latestButton->keycode; + button->key.isswitch = latestButton->isswitch; + button->key.defaultstate = latestButton->defaultstate; + button->image.height /= 2; + button->tooltip = latestButton->tooltip; + // Switches have 4 pictures + if (button->key.isswitch) + button->image.height /= 2; + button->key.width = button->image.width; + button->key.height = button->image.height; + if (layout->keyboard && layout->keyboard->switchcode != 0 && + layout->keyboard->switchcode == button->key.keycode) { + int state = button->key.state; + if (skin->keyboard.button) state = skin->keyboard.button->key.state; + skin->keyboard.button = button; + skin->keyboard.button->key.state = state; + } + } + + latestButton = latestButton->next; + if(latestButton) { + button->next = (SkinButton*)qemu_mallocz(sizeof(SkinButton)); + button = button->next; + } + } + return 0; +} + +static void skin_free_image(SkinImage** image) +{ + if (!(*image)) return; + if ((*image)->data) { + qemu_free((*image)->data); + (*image)->data = NULL; + } + qemu_free(*image); + *image = NULL; +} + +static void skin_free_keys(SkinKey** keys) +{ + SkinKey* key = *keys; + while(key) { + *keys = key->next; + qemu_free(key); + key = *keys; + *keys = NULL; + } +} + +static void skin_free_buttons(SkinButton** buttons) +{ + SkinButton* button = *buttons; + while(button) { + *buttons = button->next; + if (button->image.data) { + qemu_free(button->image.data); + button->image.data = NULL; + } + qemu_free(button); + button = *buttons; + *buttons = NULL; + } +} + +int skin_activate_layout(SkinScreen* skin, int rotation) +{ + int result = -1; + if (!skin->config->landscape && !skin->config->portrait) { + //printf("skin_config.c: skin_activate_layout, no landscape or portrait\n"); + return result; + } + printf("skin_config.c: skin_activate_layout, rotation=%d\n", rotation); + SkinLayout* layout = skin->config->landscape; + if (!skin->config->landscape) { + layout = skin->config->portrait; + // Only portrait possible + skin->rotation_req = skin->rotation = on; + } + if (rotation == on) { + if (skin->config->portrait) { + layout = skin->config->portrait; + skin->rotation_req = skin->rotation = off; + } + // Only landscape possible + else skin->rotation_req = skin->rotation = on; + } else { + skin->rotation_req = skin->rotation = on; + } + if (layout->width == 0 || layout->height == 0) { + // No value given in XML, use some default + //printf("Default layout values\n"); + layout->width = 1200; + layout->height = 650; + } + if (layout->emuscreen_width == 0 || layout->emuscreen_height == 0) { + // No default value given, make it 800x480 then + layout->emuscreen_width = 320; + layout->emuscreen_height = 480; + } + skin->width = layout->width; + skin->height = layout->height; + memcpy(&skin->bgcolor, &layout->bgcolor, sizeof(SkinBackgroundColor)); + if (layout->keyboard) + skin->keyboard.animated = layout->keyboard->animated; + + skin->es->posx = layout->emuscreen_posx; + skin->es->posy = layout->emuscreen_posy; + skin->es->width = layout->emuscreen_width; + skin->es->height = layout->emuscreen_width; + + // Store pointers to obsolete data + SkinImage* curr_background = skin->background; + SkinButton* curr_buttons = skin->buttons; + SkinImage* curr_keyboard = skin->keyboard.image; + SkinKey* curr_keys = skin->keyboard.keys; + + // Copy all the keys + if (layout->keyboard) { + if (layout->keyboard->keys) + skin->keyboard.keys = (SkinKey*)qemu_mallocz(sizeof(SkinKey)); + SkinKey *key = skin->keyboard.keys; + SkinKeyConfig* keyc = layout->keyboard->keys; + while(keyc) { + key->posx = keyc->posx; + key->posy = keyc->posy; + key->width = keyc->width; + key->height = keyc->height; + key->keycode = keyc->keycode; + keyc = keyc->next; + if (keyc) { + key->next = (SkinKey*)qemu_mallocz(sizeof(SkinKey)); + key = key->next; + } + } + } + result = skin_load_images(skin, layout); + + // Free obsolete data + skin_free_image(&curr_background); + skin_free_buttons(&curr_buttons); + skin_free_image(&curr_keyboard); + skin_free_keys(&curr_keys); + + return result; +} + +static void parser_start_hndl(void *data, const char *element, const char **attr) +{ + //printf("parser_start_hndl, element=%s\n", element); + SkinParser *parserconfig = (SkinParser*)data; + SkinLayout *layout = NULL; + SkinButtonConfig *latestButton = NULL; + SkinKeyConfig *latestKey = NULL; + int i = 0; + unsigned int hex; + // Print XML file content for testing + /*for (i = 0; attr[i]; i += 2) { + printf("attr %s='%s'\n", attr[i], attr[i + 1]); + }*/ + + if (value_is(element, "root")) { + // Initialize the pointers + layout = NULL; + latestButton = NULL; + latestKey = NULL; + return; + } + + if (value_is(element, "landscape")) { + if(parserconfig->layout != NULL) { + fprintf(stderr, "XML Parsing failure unexpected nesting of %s\n", element); + exit(1); + } + parserconfig->skin->config->landscape = + (SkinLayout*) qemu_mallocz(sizeof(SkinLayout)); + parserconfig->layout = parserconfig->skin->config->landscape; + } + + if (value_is(element, "portrait")) { + if(parserconfig->layout != NULL) { + fprintf(stderr, "XML Parsing failure unexpected nesting of %s\n", element); + exit(1); + } + parserconfig->skin->config->portrait = + (SkinLayout*) qemu_mallocz(sizeof(SkinLayout)); + parserconfig->layout = parserconfig->skin->config->portrait; + } + + if (parserconfig) layout = parserconfig->layout; + + if (value_is(element, "tooltip")) { + parserconfig->skin->tooltip.color |= 0xFF000000; // Full alpha channel + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "red")) { + parserconfig->skin->tooltip.color |= atoi(attr[i + 1]) << 16; + } + if (value_is(attr[i], "green")) { + parserconfig->skin->tooltip.color |= atoi(attr[i + 1]) << 8; + } + if (value_is(attr[i], "blue")) { + parserconfig->skin->tooltip.color |= atoi(attr[i + 1]); + } + } + } + + if (value_is(element, "font")) { + // We have a font, allocate it + SkinFont* font = parserconfig->skin->font = (SkinFont*)qemu_mallocz(sizeof(SkinFont)); + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "width")) { + font->char_width = atoi(attr[i + 1]); + } + if (value_is(attr[i], "height")) { + font->char_height = atoi(attr[i + 1]); + } + if (value_is(attr[i], "image")) { + // We load the font image directly, first construct the path + char* filepath = (char*)qemu_malloc( + strlen(parserconfig->filepath) + + strlen(attr[i + 1]) + 1); + strcpy(filepath, parserconfig->filepath); + strcat(filepath, attr[i + 1]); + font->image = skin_load_image(filepath); + qemu_free(filepath); + } + } + // If we don't have an image, then no font + if (!font->image || font->char_width == 0 || font->char_height == 0) { + parserconfig->skin->font = NULL; + qemu_free(font); + } + } + + if (value_is(element, "skin")) { + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "width")) { + layout->width = atoi(attr[i + 1]); + } + if (value_is(attr[i], "height")) { + layout->height = atoi(attr[i + 1]); + } + } + } + + if (value_is(element, "bgcolor")) { + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "red")) { + layout->bgcolor.red = atoi(attr[i + 1]); + } + if (value_is(attr[i], "green")) { + layout->bgcolor.green = atoi(attr[i + 1]); + } + if (value_is(attr[i], "blue")) { + layout->bgcolor.blue = atoi(attr[i + 1]); + } + } + } + if (value_is(element, "background")) { + if (!layout->background) { + layout->background = + (SkinImageConfig*) qemu_mallocz(sizeof(SkinImageConfig)); + layout->background->file = NULL; + } + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "image")) { + layout->background->file = qemu_mallocz(strlen(parserconfig->filepath) + + strlen(attr[i + 1]) + 1); + strcpy(layout->background->file, parserconfig->filepath); + strcat(layout->background->file, attr[i + 1]); + //printf("layout->background->file='%s'\n",layout->background->file); + } + if (value_is(attr[i], "posx")) { + layout->background->posx = atoi(attr[i + 1]); + } + if (value_is(attr[i], "posy")) { + layout->background->posy = atoi(attr[i + 1]); + } + } + } + if (value_is(element, "screen")) { + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "posx")) { + layout->emuscreen_posx = atoi(attr[i + 1]); + } + if (value_is(attr[i], "posy")) { + layout->emuscreen_posy = atoi(attr[i + 1]); + } + if (value_is(attr[i], "width")) { + layout->emuscreen_width = atoi(attr[i + 1]); + } + if (value_is(attr[i], "height")) { + layout->emuscreen_height = atoi(attr[i + 1]); + } + } + //layout->emuscreen_posx, layout->emuscreen_posy,layout->emuscreen_width, layout->emuscreen_height); + } + + if (value_is(element, "keyboard")) { + if (!layout->keyboard) { + layout->keyboard = + (SkinKeyboardConfig*)qemu_mallocz(sizeof(SkinKeyboardConfig)); + layout->keyboard->image = + (SkinImageConfig*) qemu_mallocz(sizeof(SkinImageConfig)); + layout->keyboard->image->file = NULL; + } + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "image")) { + layout->keyboard->image->file = qemu_mallocz(strlen(parserconfig->filepath) + + strlen(attr[i + 1]) + 1); + strcpy(layout->keyboard->image->file, parserconfig->filepath); + strcat(layout->keyboard->image->file, attr[i + 1]); + } + if (value_is(attr[i], "animated")) { + if (value_is(attr[i + 1], "yes")) + layout->keyboard->animated = 1; + } + if (value_is(attr[i], "posx")) { + layout->keyboard->image->posx = atoi(attr[i + 1]); + } + if (value_is(attr[i], "posy")) { + layout->keyboard->image->posy = atoi(attr[i + 1]); + } + if (value_is(attr[i], "switchkey")) { + layout->keyboard->switchcode = atoi(attr[i + 1]); + } + if (value_is(attr[i], "screenwidth")) { + layout->keyboard->screenwidth = atoi(attr[i + 1]); + } + if (value_is(attr[i], "screenheight")) { + layout->keyboard->screenheight = atoi(attr[i + 1]); + } + if (value_is(attr[i], "red")) { + layout->keyboard->highlight_red = atoi(attr[i + 1]); + } + if (value_is(attr[i], "green")) { + layout->keyboard->highlight_green = atoi(attr[i + 1]); + } + if (value_is(attr[i], "blue")) { + layout->keyboard->highlight_blue = atoi(attr[i + 1]); + } + if (value_is(attr[i], "offset")) { + layout->keyboard->offset = atoi(attr[i + 1]); + } + } + } + if (value_is(element, "key")) { + if (!layout->keyboard->keys) { + //printf("first key\n"); + layout->keyboard->keys = (SkinKeyConfig*) qemu_mallocz(sizeof(SkinKeyConfig)); + latestKey = layout->keyboard->keys; + } + else { + //printf("not the first key\n"); + latestKey = layout->keyboard->keys; + while (latestKey->next) latestKey = latestKey->next; + latestKey->next = (SkinKeyConfig*) qemu_mallocz(sizeof(SkinKeyConfig)); + latestKey = latestKey->next; + } + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "posx")) { + latestKey->posx = atoi(attr[i + 1]); + } + if (value_is(attr[i], "posy")) { + latestKey->posy = atoi(attr[i + 1]); + } + if (value_is(attr[i], "width")) { + latestKey->width = atoi(attr[i + 1]); + } + if (value_is(attr[i], "height")) { + latestKey->height = atoi(attr[i + 1]); + } + if (value_is(attr[i], "keycode")) { + sscanf(attr[i + 1], "%X", &hex); + latestKey->keycode = hex; + } + } + } + if (value_is(element, "button")) { + if (!layout->buttons) { + //printf("first button\n"); + layout->buttons = (SkinButtonConfig*)qemu_mallocz(sizeof(SkinButtonConfig)); + latestButton = layout->buttons; + } + else { + //printf("not the first button\n"); + latestButton = layout->buttons; + while( latestButton->next ) latestButton = latestButton->next; + latestButton->next = (SkinButtonConfig*)qemu_mallocz(sizeof(SkinButtonConfig)); + latestButton = latestButton->next; + } + //printf("not the first button 2\n"); + for (i = 0; attr[i]; i += 2) { + if (value_is(attr[i], "image")) { + latestButton->image.file = (char*)qemu_malloc(strlen(attr[i + 1]) + + strlen(parserconfig->filepath) + 1); + strcpy(latestButton->image.file, parserconfig->filepath); + strcat(latestButton->image.file, attr[i + 1]); + //printf("button file='%s'\n", latestButton->image.file); + } + if (value_is(attr[i], "posx")) { + latestButton->image.posx = atoi(attr[i + 1]); + //printf("posx=%d\n", latestButton->image.posx); + } + if (value_is(attr[i], "posy")) { + latestButton->image.posy = atoi(attr[i + 1]); + //printf("posy=%d\n", latestButton->image.posy); + } + if (value_is(attr[i], "keycode")) { + latestButton->keycode = atoi(attr[i + 1]); + //printf("keycode=0x%x\n",latestButton->keycode); + } + if (value_is(attr[i], "switch")) { + if (value_is(attr[i + 1], "yes")) { + latestButton->isswitch = 1; + latestButton->defaultstate = undefined; + } + //printf("switch=%s\n", latestButton->isswitch ? "yes":"no"); + } + if (value_is(attr[i], "defaultstate")) { + if (value_is(attr[i + 1], "on")) latestButton->defaultstate = on; + if (value_is(attr[i + 1], "off")) latestButton->defaultstate = off; + //printf("defaultstate=%d\n", latestButton->defaultstate); + } + if (value_is(attr[i], "action")) { + latestButton->tooltip = (char*)qemu_malloc(strlen(attr[i + 1]) + 1); + strcpy(latestButton->tooltip, attr[i + 1]); + } + } + } +} + + +static void parser_end_hndl(void *data, const char *element) +{ + //printf("parser_end_hndl, element=%s\n", element); + SkinParser *parserconfig = (SkinParser*)data; + if (value_is(element, "landscape")) { + if(parserconfig->layout != parserconfig->skin->config->landscape) { + fprintf(stderr, "Unexpected ending of %s in XML file\n", element); + exit(1); + } + parserconfig->layout = NULL; + } + + if (value_is(element, "portrait")) { + if(parserconfig->layout != parserconfig->skin->config->portrait) { + fprintf(stderr, "Unexpected ending of %s in XML file\n", element); + exit(1); + } + parserconfig->layout = NULL; + } +} + + +static int skin_load_file(SkinScreen* skin, char *file) +{ + FILE* skin_xml; + char buffer[BUFFER_SIZE]; + int done; + XML_Parser parser = XML_ParserCreate(NULL); + if (parser) { + SkinParser parserconfig; + parserconfig.skin = skin; + parserconfig.layout = NULL; + + XML_SetUserData( parser, (void*)&parserconfig ); + XML_SetElementHandler(parser, parser_start_hndl, parser_end_hndl); + + skin_xml = fopen(file, "r"); + //printf("skin_config_c: skin_load_file = '%s'\n", file); + if (!skin_xml) { + fprintf(stderr, "Error opening skin file '%s'\n", file); + XML_ParserFree(parser); + return -1; + } + // Extract the path of the given XML file to be used as a default path + char *p = strrchr(file, '/'); + if (p) { + int index = p - file + 1; + p = qemu_mallocz(index + 1); + strncpy(p, file, index); + // last character already zeroed at malloc + } else { + p = qemu_mallocz(3); + p[0] = '.'; + p[1] = '/'; + // last character already zeroed at malloc + } + + parserconfig.filepath = p; + XML_SetUserData(parser, (void*)&parserconfig); + XML_SetElementHandler(parser, parser_start_hndl, parser_end_hndl); + do { + if (fgets(buffer, sizeof(buffer), skin_xml) == NULL) + done = 1; + else { + done = feof(skin_xml); + if (!XML_Parse(parser, buffer, strlen(buffer), done)) { + fprintf(stderr, "Parse error at line %d: %s\n", + XML_GetCurrentLineNumber(parser), + XML_ErrorString(XML_GetErrorCode(parser))); + // Continue anyway + } + } + } while (!done); + + if (done && !feof(skin_xml)) { + fprintf(stderr, "Parse error, unexpected EOF\n"); + // Continue anyway + } + + XML_ParserFree(parser); + qemu_free(parserconfig.filepath); + + // Create a buffer to store the information + fclose(skin_xml); + return skin_activate_layout(skin, skin->rotation); + } + return -1; +} + +SkinScreen* skin_load_configuration(char* file, int portrait) +{ + //printf("skin_config.c: >> skin_load_configuration\n"); + SkinScreen *skin = (SkinScreen*) qemu_mallocz(sizeof(SkinScreen)); + if(!skin) return NULL; + skin->es = (EmulatedScreen*) qemu_mallocz(sizeof(EmulatedScreen)); + //printf("skin_load_configuration 1\n"); + if(!skin->es) { + qemu_free(skin); + return NULL; + } + skin->config = (SkinConfig*) qemu_mallocz(sizeof(SkinConfig)); + //printf("skin_load_configuration 2\n"); + if (!skin->config) { + qemu_free(skin->es); + qemu_free(skin); + return NULL; + } + if (portrait) { + skin->rotation_req = skin->rotation = on; + } + if(skin_load_file(skin, file)) { + //printf("skin_config.c: skin_load_configuration, skin loaded '%s'\n", file); + qemu_free(skin->config); + qemu_free(skin->es); + qemu_free(skin); + skin = NULL; + } + if (skin && skin->keyboard.button) + skin->keyboard.button->key.state = ESkinBtn_Active; + + return skin; +} diff --git a/skin/skin_config.h b/skin/skin_config.h new file mode 100644 index 000000000..7b0d693f4 --- /dev/null +++ b/skin/skin_config.h @@ -0,0 +1,29 @@ +/* + * Skin configuration reading header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "skin.h" + +#ifndef SKIN_CONFIG_H__ +#define SKIN_CONFIG_H__ + +SkinScreen *skin_load_configuration(char* file, int portrait); +int skin_activate_layout(SkinScreen* skin, int rotation); + +#endif /* SKIN_CONFIG_H__ */ diff --git a/skin/skin_image.c b/skin/skin_image.c new file mode 100644 index 000000000..6b116c799 --- /dev/null +++ b/skin/skin_image.c @@ -0,0 +1,490 @@ +/* + * Skin image drawing + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "qemu-common.h" +#ifndef CONFIG_COCOA +#define PNG_SKIP_SETJMP_CHECK +#include +#endif + +#include "console.h" + +#include "skin_image.h" + +#define PIXEL_DST 2 +#include "skin_image_template.h" +#undef PIXEL_DST +#define PIXEL_DST 4 +#include "skin_image_template.h" +#undef PIXEL_DST + +// Font defines, ascii table font from ' ' to '~' characters supported +#define CHAR_BASE ' ' // base number (first character in table) +#define CHAR_LAST '~' // character in the font picture + +#define TOOLTIP_SPAC_W 3 // Spacing of the tooltip on each side +#define TOOLTIP_SPAC_H 1 // Spacing of the tooltip on each side + +static void skin_merge_image( SkinImage *dst, SkinImage *src, + int xd, int yd, // Destination + int xs, int ys, int w, int h) // Source +{ + int line; + // This will fit, now copy line by line + uint32_t r, g, b, a, px; + uint32_t *srcpx, *dstpx; + for( line = 0; line < h; line++) { + dstpx = (uint32_t*)(dst->data + // base destination + ((yd + line) * dst->linesize) + // pixel row + (xd * dst->pf.bytes_per_pixel)); // pixel column start + srcpx = (uint32_t*)(src->data + // base source + src->linesize * (ys + line) + // pixel row + xs * dst->pf.bytes_per_pixel); // pixel column start + for (px = 0; px < w; px++) { + // Extract color values and alpha channel + r = ((srcpx[px]) & src->pf.rmask) >> (src->pf.rshift); + g = ((srcpx[px]) & src->pf.gmask) >> (src->pf.gshift); + b = ((srcpx[px]) & src->pf.bmask) >> (src->pf.bshift); + a = ((srcpx[px]) & src->pf.amask) >> (src->pf.ashift); + + // Apply alpha blend factor + r = r * a / src->pf.amax; + g = g * a / src->pf.gmax; + b = b * a / src->pf.bmax; + // Convert to destination depth + r = r >> (8 - dst->pf.rbits); + g = g >> (8 - dst->pf.gbits); + b = b >> (8 - dst->pf.bbits); + // Merge the colors + r |= ((dstpx[px] & dst->pf.rmask) >> dst->pf.rshift) * + (dst->pf.rmax - (a >> (8 - dst->pf.rbits))) / dst->pf.rmax; + g |= ((dstpx[px] & dst->pf.gmask) >> dst->pf.gshift) * + (dst->pf.gmax - (a >> (8 - dst->pf.gbits))) / dst->pf.gmax; + b |= ((dstpx[px] & dst->pf.bmask) >> dst->pf.bshift) * + (dst->pf.bmax - (a >> (8 - dst->pf.bbits))) / dst->pf.bmax; + a = a >> (8 - dst->pf.abits); + a = a + ((dstpx[px] & dst->pf.amask) >> dst->pf.ashift) * + (dst->pf.amax - (a >> (8 - dst->pf.abits))) / dst->pf.amax; + + // Put the color as destination pixel + dstpx[px] = ((r << dst->pf.rshift) & dst->pf.rmask) | + ((g << dst->pf.gshift) & dst->pf.gmask) | + ((b << dst->pf.bshift) & dst->pf.bmask) | + ((a << dst->pf.ashift) & dst->pf.amask); + + } + } +} + +SkinImage* skin_image_createtext(SkinScreen* skin, char* text) +{ + if (skin->font && skin->font->char_width != 0) { + int cur, line; + int len = strlen(text); + if (len <= 0) return NULL; + struct SkinImage* image = (SkinImage*)qemu_malloc(sizeof(SkinImage)); + if (image) { + memcpy(image, skin->font->image, sizeof(SkinImage)); + // Create width with spacing on each side + image->width = skin->font->char_width * len + TOOLTIP_SPAC_W * 2; + image->height += TOOLTIP_SPAC_H * 2; + image->linesize = image->width * 4; // bytes/pixel + image->data = qemu_malloc(image->width * image->height * 4); + uint32_t *cleardst; + // Full background is always black (gives us a border) + for (line = 0; line < image->height; line++) + for (cur = 0; cur < image->width; cur++) { + cleardst = (uint32_t*)(image->data + + (line * image->linesize) + (cur * 4)); + *cleardst = 0xFF000000; // Black + } + // Next fill the background color if given (defaults to transparent) + for (line = 1; line < image->height-1; line++) + for (cur = 1; cur < image->width-1; cur++) { + cleardst = (uint32_t*)(image->data + + (line * image->linesize) + (cur * 4)); + *cleardst = skin->tooltip.color; + } + + // Copy the characters in place, one at a time + for (cur = 0; cur < len; cur++) { + skin_merge_image(image, skin->font->image, + cur * skin->font->char_width + TOOLTIP_SPAC_W, TOOLTIP_SPAC_H, + (text[cur]-CHAR_BASE) * skin->font->char_width, 0, + skin->font->char_width, skin->font->char_height); + } + } + return image; + } + return NULL; +} + +void skin_rotate_buffer(SkinScreen* skin, DisplayState* ds, int x, int y, int w, int h) +{ + switch (ds_get_bytes_per_pixel(ds)) { + case 2: + skin_rotate_buffer_bytes_2(skin, ds, x, y, w, h); + break; + case 4: + skin_rotate_buffer_bytes_4(skin, ds, x, y, w, h); + break; + default: + printf("Wrong destination buffer\n"); + break; + } +} + +void skin_fill_color( SkinScreen* skin, SkinArea* area, int r, int g, int b) +{ + uint32_t color = 0; + PixelFormat* pf = &skin->ds->surface->pf; + color = (((r << (pf->rshift)) >> (8 - pf->rbits)) & pf->rmask) | + (((g << (pf->gshift)) >> (8 - pf->gbits)) & pf->gmask) | + (((b << (pf->bshift)) >> (8 - pf->bbits)) & pf->bmask); + vga_fill_rect(skin->ds, area->x, area->y, area->width, area->height, color); +} + +void skin_fill_background(SkinScreen* skin, SkinArea* area) +{ + int r = skin->bgcolor.red; + int g = skin->bgcolor.green; + int b = skin->bgcolor.blue; + skin_fill_color(skin, area, r, g, b); +} + +int skin_draw_button(SkinScreen* skin, SkinButton* button, int state) +{ + //printf("skin_draw_button( state: %d ) %d\n", state, button->key.state); + if(state == button->key.state) return 0; + if(state != ESkinBtn_forceredraw) button->key.state = state; + struct SkinImage image; + memcpy(&image, &button->image, sizeof(SkinImage)); + // Correct the data pointer + image.data += (image.linesize * image.height * button->key.state); + struct SkinArea clip = { image.posx, image.posy, image.width, image.height }; + skin_fill_background(skin, &clip); + if (skin->background != NULL) { + (void)skin_draw_image(skin, skin->background, &clip); + } + return skin_draw_image(skin, &image, &clip); +} + +int skin_draw_animated_keyboard(SkinScreen* skin, SkinImage* image, int phase) +{ + //printf("skin_draw_animated_keyboard, phase=%d\n", phase); + struct SkinImage keyboardpart; + memcpy(&keyboardpart, image, sizeof(SkinImage)); + // Correct the data pointer to draw only part of the picture + keyboardpart.data += (keyboardpart.linesize * keyboardpart.height * phase); + struct SkinArea clip = { keyboardpart.posx, keyboardpart.posy, + keyboardpart.width, keyboardpart.height }; + if (skin->background != NULL) { + (void)skin_draw_image(skin, skin->background, &clip); + } + return skin_draw_image(skin, &keyboardpart, &clip); + +} + +int skin_highlight_key(SkinScreen* skin, SkinKey* key, int state) +{ + if (state == key->state) return 0; + key->state = state; + // Do we draw the actual key image or do we highlight that area + if (state != ESkinBtn_Idle) { + uint8_t *d, *d1; + int x, y, bpp, r, g, b, a, rd, gd, bd; + bpp = (ds_get_bits_per_pixel(skin->ds) + 7) >> 3; + d1 = ds_get_data(skin->ds) + + ds_get_linesize(skin->ds) * key->posy + bpp * key->posx; + + PixelFormat* dpf = &skin->ds->surface->pf; + // Apply alpha blend factor + a = 128; + r = skin->keyboard.highlight_red * a / 255; + g = skin->keyboard.highlight_green * a / 255; + b = skin->keyboard.highlight_blue * a / 255; + // Convert to destination depth + r = r >> (8 - dpf->rbits); + g = g >> (8 - dpf->gbits); + b = b >> (8 - dpf->bbits); + // Apply color overlay + for (y = 0; y < key->height; y++) { + d = d1; + switch(bpp) { + case 1: + break; + case 2: + for (x = 0; x < key->width; x++) { + // Merge the colors + rd = r | (((*((uint16_t *)d)) & dpf->rmask) >> dpf->rshift) * + (dpf->rmax - (a >> (8 - dpf->rbits))) / dpf->rmax; + gd = g | ((*((uint16_t *)d) & dpf->gmask) >> dpf->gshift) * + (dpf->gmax - (a >> (8 - dpf->gbits))) / dpf->gmax; + bd = b | ((*((uint16_t *)d) & dpf->bmask) >> dpf->bshift) * + (dpf->bmax - (a >> (8 - dpf->bbits))) / dpf->bmax; + // Put the color as destination pixel + *((uint16_t *)d) = ((rd << dpf->rshift) & dpf->rmask) | + ((gd << dpf->gshift) & dpf->gmask) | ((bd << dpf->bshift) & dpf->bmask); + d += 2; + } + break; + case 4: + for (x = 0; x < key->width; x++) { + // Merge the colors + rd = r | ((*((uint32_t *)d) & dpf->rmask) >> dpf->rshift) * + (dpf->rmax - (a >> (8 - dpf->rbits))) / dpf->rmax; + gd = g | ((*((uint32_t *)d) & dpf->gmask) >> dpf->gshift) * + (dpf->gmax - (a >> (8 - dpf->gbits))) / dpf->gmax; + bd = b | ((*((uint32_t *)d) & dpf->bmask) >> dpf->bshift) * + (dpf->bmax - (a >> (8 - dpf->bbits))) / dpf->bmax; + // Put the color as destination pixel + *((uint32_t *)d) = ((rd << dpf->rshift) & dpf->rmask) | + ((gd << dpf->gshift) & dpf->gmask) | ((bd << dpf->bshift) & dpf->bmask); + d += 4; + } + break; + } + d1 += ds_get_linesize(skin->ds); + } + } + else { + SkinArea area = { key->posx, key->posy, key->width, key->height }; + skin_fill_background(skin, &area); + skin_draw_image( skin, skin->keyboard.image, &area); + } + return 1; +} + +SkinArea skin_cliparea(SkinImage* image, SkinArea* area) +{ + SkinArea clip; + clip.x = area->x; + clip.y = area->y; + clip.width = area->width; + clip.height = area->height; + + int imagex = image->posx + image->width; + int imagey = image->posy + image->height; + int areax = area->x + area->width; + int areay = area->y + area->height; + + if (image->posx > area->x) { + clip.x = image->posx; + clip.width -= image->posx - area->x; + } + if (image->posy > area->y) { + clip.y = image->posy; + clip.height -= image->posy - area->y; + } + if (areax > imagex) clip.width = imagex - clip.x; + if (areay > imagey) clip.height = imagey - clip.y; +// printf("%d, %d, %d, %d\n", clip.x, clip.y, clip.width, clip.height); + return clip; +} + +int skin_draw_image(SkinScreen* skin, SkinImage* image, SkinArea* area) +{ + //printf("skin_draw_image(area: %d, %d, %d %d)\nimage: %d, %d, %d, %d\nimage->data: 0x%X\n", + // area->x, area->y, area->width, area->height, + // image->posx, image->posy, image->width, image->height), (unsigned int)image->data); + // Get the clipping area we need to update + SkinArea clip = skin_cliparea(image, area); + // Determine the correct destination buffer + switch (ds_get_bytes_per_pixel(skin->ds)) { + case 2: + return skin_draw_image_from_a24_to_2(skin, image, &clip); + case 4: + return skin_draw_image_from_a24_to_4(skin, image, &clip); + default: + printf("Wrong destination buffer\n"); + return 0; + } +} + +// Load / Read png files +#ifndef CONFIG_COCOA +void *skin_loadpng(const char *fn, unsigned *_width, unsigned *_height) +{ + FILE *fp = 0; + unsigned char header[8]; + unsigned char *data = 0; + unsigned char **rowptrs = 0; + png_structp p = 0; + png_infop pi = 0; + + png_uint_32 width, height; + int bitdepth, colortype, imethod, cmethod, fmethod, i; + + p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if(p == 0) { + printf("%s: failed to allocate png read struct\n", fn); + return 0; + } + + pi = png_create_info_struct(p); + if(pi == 0) { + printf("%s: failed to allocate png info struct\n", fn); + goto oops; + } + + fp = fopen(fn, "rb"); + if(fp == 0) { + printf("%s: failed to open file\n", fn); + return 0; + } + + if(fread(header, 8, 1, fp) != 1) { + printf("%s: failed to read header\n", fn); + goto oops; + } + + if(png_sig_cmp(header, 0, 8)) { + printf("%s: header is not a PNG header\n", fn); + goto oops; + } + + if(setjmp(png_jmpbuf(p))) { + printf("%s: png library error\n", fn); + oops: + png_destroy_read_struct(&p, &pi, 0); + if(fp != 0) fclose(fp); + if(data != 0) free(data); + if(rowptrs != 0) free(rowptrs); + return 0; + } + + png_init_io(p, fp); + png_set_sig_bytes(p, 8); + + png_read_info(p, pi); + + png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype, + &imethod, &cmethod, &fmethod); +// printf("PNG: %d x %d (d=%d, c=%d)\n", +// width, height, bitdepth, colortype); + + switch(colortype){ + case PNG_COLOR_TYPE_PALETTE: + png_set_palette_to_rgb(p); + break; + + case PNG_COLOR_TYPE_RGB: + if(png_get_valid(p, pi, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(p); + } else { + png_set_filler(p, 0xFF, PNG_FILLER_AFTER); + } + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + break; + + case PNG_COLOR_TYPE_GRAY: + printf("\n"); + if(bitdepth < 8) { + png_set_gray_1_2_4_to_8(p); + } + + default: + printf("%d: unsupported (grayscale?) color type\n", colortype); + goto oops; + } + + png_set_bgr(p); + + if(bitdepth == 16) { + png_set_strip_16(p); + } + + data = (unsigned char*) malloc((width * 4) * height); + rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height); + + if((data == 0) || (rowptrs == 0)){ + printf("could not allocate data buffer\n"); + goto oops; + } + + for(i = 0; i < height; i++) { + rowptrs[i] = data + ((width * 4) * i); + } + + png_read_image(p, rowptrs); + + png_destroy_read_struct(&p, &pi, 0); + fclose(fp); + if(rowptrs != 0) free(rowptrs); + + *_width = width; + *_height = height; + + return (void*) data; +} +#endif + +static void skin_png_defaultpixelformat(SkinImage* image) +{ + image->pf.bits_per_pixel = 32; + image->pf.bytes_per_pixel = 4; + image->pf.depth = 32; + image->pf.rmask = 0x00FF0000; + image->pf.gmask = 0x0000FF00; + image->pf.bmask = 0x000000FF; + image->pf.amask = 0xFF000000; + image->pf.rshift = 16; + image->pf.gshift = 8; + image->pf.bshift = 0; + image->pf.ashift = 24; + image->pf.rmax = 255; + image->pf.gmax = 255; + image->pf.bmax = 255; + image->pf.amax = 255; + image->pf.rbits = 8; + image->pf.gbits = 8; + image->pf.bbits = 8; + image->pf.abits = 8; + image->linesize = image->pf.bytes_per_pixel * image->width; +} + +int skin_load_image_data(SkinImage* image, char* file) +{ + unsigned width, height; + image->data = skin_loadpng(file, &width, &height); + if (image->data == NULL) { + fprintf(stderr, "failed to load image '%s'\n", file ); + return 1; + } + image->width = (int)width; + image->height = (int)height; + skin_png_defaultpixelformat(image); + +// printf("image loaded '%s', width=%d, height=%d\n", file, width, height); + return 0; +} + +SkinImage* skin_load_image(char* file) +{ + SkinImage* image = (SkinImage*)qemu_mallocz(sizeof(SkinImage)); + if(!image) return NULL; + if(skin_load_image_data(image, file)) { + qemu_free(image); + return NULL; + } + return image; +} + diff --git a/skin/skin_image.h b/skin/skin_image.h new file mode 100644 index 000000000..74a15d558 --- /dev/null +++ b/skin/skin_image.h @@ -0,0 +1,44 @@ +/* + * Skin image drawing header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "skin.h" + +#ifndef SKIN_IMAGE_H__ +#define SKIN_IMAGE_H__ + +SkinImage* skin_image_createtext( SkinScreen* skin, char* text); + +void skin_rotate_buffer(SkinScreen* skin, DisplayState* ds, int x, int y, int w, int h); + +void skin_fill_color( SkinScreen* skin, SkinArea* area, int r, int g, int b); +void skin_fill_background(SkinScreen* skin, SkinArea* area); +int skin_draw_button(SkinScreen* skin, SkinButton* button, int state); +int skin_highlight_key(SkinScreen* skin, SkinKey* key, int state); +int skin_draw_image(SkinScreen* skin, SkinImage* image, SkinArea* area); +int skin_draw_animated_keyboard(SkinScreen* skin, SkinImage* image, int state); + +SkinArea skin_cliparea(SkinImage* image, SkinArea* area); + +int skin_load_image_data(SkinImage* image, char* file); +SkinImage* skin_load_image(char* file); + +void *skin_loadpng(const char *fn, unsigned *w, unsigned *h); + +#endif /* SKIN_IMAGE_H__ */ diff --git a/skin/skin_image_template.h b/skin/skin_image_template.h new file mode 100644 index 000000000..28fa3484b --- /dev/null +++ b/skin/skin_image_template.h @@ -0,0 +1,113 @@ +/* + * Skin image conversion template header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if PIXEL_DST == 2 + #define DST_TYPE uint16_t +#endif +#if PIXEL_DST == 4 + #define DST_TYPE uint32_t +#endif + +/* Function declaration depending on the bytes per pixel + either 2 bytes or 4 bytes types are used */ +static inline void glue(skin_rotate_buffer_bytes_,PIXEL_DST) + (SkinScreen* skin, DisplayState* ds, int x, int y, int w, int h) +{ + DST_TYPE *srcpixel, *dstpixel; + int px, py; + // Loop through all the pixels to rotate them 90 degr clockwise + for (py = 0; py < h; py++) { + for (px = 0; px < w; px++) { + // Source pixel (landscape orientation) + srcpixel = (DST_TYPE*)(ds_get_data(ds) + + ((y + py) * ds_get_linesize(ds)) + + ((x + px) * ds_get_bytes_per_pixel(ds))); + // Destination pixel (portrait orientation) + dstpixel = (DST_TYPE*)(ds_get_data(skin->ds) + + (skin->es->posy + x + px) * ds_get_linesize(skin->ds) + + (skin->es->posx + skin->es->height - (y+py)) * + ds_get_bytes_per_pixel(skin->ds)); + // Copy the pixel data + *dstpixel = *srcpixel; + } + } +} + +static inline int glue(skin_draw_image_from_a24_to_,PIXEL_DST) + (SkinScreen* skin, SkinImage* image, SkinArea* clip) +{ + int line; + // Calculate source row and column start + int colst = (clip->x - image->posx) * image->pf.bytes_per_pixel; + int rowst = (clip->y - image->posy); + // Determine number of bytes to copy + if (image && skin->ds && + ds_get_width(skin->ds) >= clip->x + clip->width && + ds_get_height(skin->ds) >= clip->y + clip->height ) { + // This will fit, now copy line by line + uint32_t r, g, b, a, px; + uint32_t* srcpx; + DST_TYPE* dstpx; + PixelFormat* dpf = &skin->ds->surface->pf; + for( line = 0; line < clip->height; line++) { + dstpx = (DST_TYPE*)(ds_get_data(skin->ds) + // base destination + ((clip->y + line) * ds_get_linesize(skin->ds)) + // pixel row + (clip->x * ds_get_bytes_per_pixel(skin->ds))); // pixel column start + srcpx = (uint32_t*)(image->data + // base source + image->linesize * (rowst + line) + // pixel row + colst); // pixel column start + for (px = 0; px < clip->width; px++) { + // Extra color values and alpha channel + r = ((srcpx[px]) & image->pf.rmask) >> (image->pf.rshift); + g = ((srcpx[px]) & image->pf.gmask) >> (image->pf.gshift); + b = ((srcpx[px]) & image->pf.bmask) >> (image->pf.bshift); + a = ((srcpx[px]) & image->pf.amask) >> (image->pf.ashift); + // Apply alpha blend factor + r = r * a / image->pf.amax; + g = g * a / image->pf.gmax; + b = b * a / image->pf.bmax; + // Convert to destination depth + r = r >> (8 - dpf->rbits); + g = g >> (8 - dpf->gbits); + b = b >> (8 - dpf->bbits); + // Merge the colors + r |= ((dstpx[px] & dpf->rmask) >> dpf->rshift) * + (dpf->rmax - (a >> (8 - dpf->rbits))) / dpf->rmax; + g |= ((dstpx[px] & dpf->gmask) >> dpf->gshift) * + (dpf->gmax - (a >> (8 - dpf->gbits))) / dpf->gmax; + b |= ((dstpx[px] & dpf->bmask) >> dpf->bshift) * + (dpf->bmax - (a >> (8 - dpf->bbits))) / dpf->bmax; + // Put the color as destination pixel + dstpx[px] = ((r << dpf->rshift) & dpf->rmask) | + ((g << dpf->gshift) & dpf->gmask) | + ((b << dpf->bshift) & dpf->bmask); + } + } + return 1; + } + else { + printf("That wouldn't fit!\n"); + } + return 0; +} + +#undef SRC_TYPE +#undef DST_TYPE + diff --git a/skin/skin_switchstate.h b/skin/skin_switchstate.h new file mode 100644 index 000000000..0a0a04960 --- /dev/null +++ b/skin/skin_switchstate.h @@ -0,0 +1,35 @@ +/* + * Skin button slider switchstate handling header + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + #ifndef SKIN_SWITCHSTATE_H + #define SKIN_SWITCHSTATE_H + + typedef enum SkinSwitchState { + unknown = -1, + inactive = 0, + active + } SkinSwitchState; + +typedef int switchstate_callback(void *opaque, const int keycode); + + void qemu_skin_add_switchstate_callback(switchstate_callback *callback, + void *opaque); + +#endif diff --git a/skin/skinning.c b/skin/skinning.c new file mode 100755 index 000000000..a9e34b79b --- /dev/null +++ b/skin/skinning.c @@ -0,0 +1,1056 @@ +/* + * Qemu main skinning + * + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include + +#include "skin.h" +#include "skin_config.h" +#include "skin_image.h" +#include "skin_button.h" +#include "qemu-timer.h" +#include "qemu_socket.h" +#include "console.h" +#include + +struct SkinScreen *skin = NULL; +// Timer for animated keyboard +static QEMUTimer *keyboard_timer = NULL; +static int keyboard_animation_phase = 0; +#define ANIM_NOT_USED 13 // Defined instead of const int because of case-usage +static int no_keyboard_anim = 0; +const int ZOOM_STEP = 10; +const int ZOOM_INDICATOR_POS = 26; +const int ZOOM_MIN_FACTOR = 50; +const int ZOOM_MAX_FACTOR = 130; +static int zoom_factor = 100; +static int first_keyboard_check = 1; +static int startup = 1; +static int rct_sock = -1; + +// #jun:hacking +DisplayState *es_ds = NULL; +int es_posx, es_posy; + +// Local prototypes +static void setup_keyboard_animation_timer(void); +static void skin_update_keyboard(void *opaque); +static void skin_draw_skin(SkinArea* area); +static void skin_cleartooltip(void *opaque); +static void skin_handle_zooming(void); +static void skin_position_items(int move); +static void skin_rct_serve(void *opaque); +static int skin_rct_initialize(int rctport); + +// Local prototypes used externally +int skinning_init(char* skin_file, int portrait, int rctport); +void skin_toggle_full_screen(DisplayState *ds); +// Overruled functions from console.c +DisplayState *qemu_graphic_console_init(vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + vga_hw_text_update_ptr text_update, + void *opaque); + +void original_qemu_console_resize(DisplayState *ds, int width, int height); + +static void skin_handle_rotation(void) +{ + if (skin->rotation != skin->rotation_req) { + skin_cleartooltip(NULL); + skin->rotation = skin->rotation_req; + skin_activate_layout(skin, skin->rotation); + SkinButton* b = skin->buttons; + while (b) { + b->key.defaultstate = undefined; + b = b->next; + } + no_keyboard_anim = 1; + // Update keyboard if needed + if (skin->keyboard.button && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + // Check if the skin offset should be applied + if (skin->rotation == on && skin->keyboard.offset != 0) { + skin_position_items(1); + } + // Resizing screen and updating keyboard without animation + if (keyboard_timer) qemu_del_timer(keyboard_timer); + original_qemu_console_resize(skin->ds, skin->keyboard.screenwidth, + skin->keyboard.screenheight); + skin_update_keyboard(NULL); + skin_handle_zooming(); + } + else { + // No keyboard needs to be drawn + if (keyboard_timer) qemu_del_timer(keyboard_timer); + original_qemu_console_resize(skin->ds, skin->width, skin->height); + skin_handle_zooming(); + } + if (skin->rotation) { + kbd_put_keycode(0x4f); // x -= axis_max + kbd_put_keycode(0x4c); // y += axis_max + kbd_put_keycode(0x4f | 0x80); + kbd_put_keycode(0x4c | 0x80); + } else { + kbd_put_keycode(0x50); // x += axis_max + kbd_put_keycode(0x4b); // y -= axis_max + kbd_put_keycode(0x50 | 0x80); + kbd_put_keycode(0x4b | 0x80); + } + } +} + +static int skin_overlaps(SkinImage* image, SkinArea* area) +{ + if (image->posx + image->width < area->x) return 0; + if (image->posy + image->height < area->y) return 0; + if (area->x + area->width < image->posx) return 0; + if (area->y + area->height < image->posy) return 0; + return 1; +} + +static void calculate_tooltip_position( SkinButton* button, + SkinImage* tooltip ) +{ + // Determine the tooltip position + // Best would be in the center below the button + int x = 0, y = 0; + x = button->image.posx + button->image.width / 2 - tooltip->width / 2; + y = button->image.posy + button->image.height; + // If it moves out top or left, move it to the edge + if (x < 0) x = 2; + if (y < 0) y = 2; + // If it moves out bottom or right, move it to the edge + if (x + tooltip->width > ds_get_width(skin->ds)) + x = ds_get_width(skin->ds) - (tooltip->width + 2); + if (y + tooltip->height > ds_get_height(skin->ds)) + y = ds_get_height(skin->ds) - (tooltip->height + 2); + // Put the tooltip position to the tooltip image + tooltip->posx = x; + tooltip->posy = y; +} + +static void skin_draw_tooltip(SkinArea *clip) +{ + if (skin->tooltip.image && + skin_overlaps(skin->tooltip.image, clip) ) { + skin_draw_image(skin, skin->tooltip.image, clip); + } +} + +static void skin_cleartooltip(void *opaque) +{ + // Clear the timer and tooltip + if (skin->tooltip.timer) { + qemu_del_timer(skin->tooltip.timer); + } + skin->tooltip.button = NULL; + if (skin->tooltip.image) { + SkinArea clip = { skin->tooltip.image->posx, + skin->tooltip.image->posy, + skin->tooltip.image->width, + skin->tooltip.image->height }; + if (skin->tooltip.image->data) qemu_free(skin->tooltip.image->data); + qemu_free(skin->tooltip.image); + skin->tooltip.image = NULL; + // Redraw the skin + skin_draw_skin(&clip); + // Make sure the emulated screen is redrawn if needed + if (skin->es) { + vga_hw_invalidate(); + vga_hw_update(); + } + dpy_update(skin->ds, clip.x, clip.y, clip.width, clip.height); + } +} + +static void skin_handle_tooltip_timeout(void *opaque) +{ + // Time to show the tooltip + SkinTooltip* tooltip = (SkinTooltip*)opaque; + if (tooltip->button) { + // Create the correct text image for the button + SkinImage *image = + skin_image_createtext(skin, tooltip->button->tooltip); + if (image) { + // Find the correct location to draw the tooltip + calculate_tooltip_position(tooltip->button, image); + // Everything set, store the image and have it drawn + tooltip->image = image; + struct SkinArea clip = { image->posx, image->posy, + image->width, image->height }; + skin_draw_tooltip(&clip); + dpy_update(skin->ds, image->posx, image->posy, + image->width, image->height); + } + } +} + +static void skin_mouse_event(void *opaque, + int x, int y, int z, int buttons_state) +{ + if (!is_graphic_console()) return; + + static int in_screen = 0; + + skin->mouse_event = on; + // Mouse position mx/my in range 0-32767, equals pixel 0 to width/height-1 + // actual mouse x and y on the application + int mx = x; + int my = y; + x = (mx - skin->es->posx) * 0x7FFF / ds_get_width(skin->es->ds); + y = (my - skin->es->posy) * 0x7FFF / ds_get_height(skin->es->ds); + +// printf("skin_mouse_event: %d, %d btn: %d (w:%d, h:%d)\n", x, y, buttons_state, +// ds_get_width(skin->ds), ds_get_height(skin->ds)); + QEMUPutMouseEntry *child = (QEMUPutMouseEntry *)opaque; +// printf("skin_mouse_event: mx:%d, my:%d, posx:%d, posy:%d, relw: %d, relh: %d, w:%d, h:%d\n", mx, my, skin->es->posx, skin->es->posy, ds_get_width(skin->es->ds), ds_get_height(skin->es->ds), skin->es->width, skin->es->height); + if (skin->rotation == off) { + if (mx > skin->es->posx && + mx < skin->es->posx + ds_get_width(skin->es->ds) /*skin->es->width*/ && + my > skin->es->posy && + my < skin->es->posy + ds_get_height(skin->es->ds) /*skin->es->height*/ ) { +// printf("skin_mouse_event: report x: %d, y: %d\n", mx - skin->es->posx, my - skin->es->posy); + + if (!in_screen) { + in_screen = 1; + SDL_ShowCursor(0); + } + + child->qemu_put_mouse_event( child->qemu_put_mouse_event_opaque, + (mx - skin->es->posx) * 0x7FFF / ds_get_width(skin->es->ds), + (my - skin->es->posy) * 0x7FFF / ds_get_height(skin->es->ds), + z, buttons_state); + } else { + if (in_screen) { + in_screen = 0; + SDL_ShowCursor(1); + } + + } + } + else { + // We have to change the coordinates, since we draw rotated + if (mx > skin->es->posx && + mx < skin->es->posx + skin->es->height && + my > skin->es->posy && + my < skin->es->posy + skin->es->width ) { + + child->qemu_put_mouse_event( child->qemu_put_mouse_event_opaque, + y, -x, z, buttons_state); + SDL_ShowCursor(0); + } else { + SDL_ShowCursor(1); + } + } + + // Check if we pressed any button + SkinButton* button = skin->buttons; + while (button) { + if (skin_button_mouse_over(&button->key, mx, my)) { + // Check if we handle start tooltip handling + if (button->tooltip && + skin->tooltip.button != button) { + skin_cleartooltip(NULL); + skin->tooltip.button = button; + if (!skin->tooltip.timer) { + skin->tooltip.timer = qemu_new_timer_ns(rt_clock, + skin_handle_tooltip_timeout, + &skin->tooltip); + } + // Set expiration time to be 1 second + qemu_mod_timer(skin->tooltip.timer, qemu_get_clock_ns(rt_clock) + 1000); + } + int state = skin_button_handle_mouse(&button->key, buttons_state & MOUSE_EVENT_LBUTTON ); + if (skin_draw_button(skin, button, state)) { + dpy_update(skin->ds, button->image.posx, button->image.posy, + button->image.width, button->image.height); + } + } + else { + if (button == skin->tooltip.button) { + skin_cleartooltip(NULL); + } + int state = skin_button_handle_mouseleave(&button->key); + if (skin_draw_button(skin, button, state)) { + dpy_update(skin->ds, button->image.posx, button->image.posy, + button->image.width, button->image.height); + } + } + button = button->next; + } + // Check if we pressed any key from the keyboard + if (skin->keyboard.button && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + SkinKey* key = skin->keyboard.keys; + while (key) { + if (skin_button_mouse_over(key, mx, my)) { + int state = skin_button_handle_mouse(key, buttons_state & MOUSE_EVENT_LBUTTON); + if (skin_highlight_key(skin, key, state)) { + dpy_update(skin->ds, key->posx, key->posy, + key->width, key->height); + } + } + else { + int state = skin_button_handle_mouseleave(key); + if (skin_highlight_key(skin, key, state)) { + dpy_update(skin->ds, key->posx, key->posy, + key->width, key->height); + } + } + key = key->next; + } + } + + skin_handle_rotation(); + skin->mouse_event = off; +} + +static void skin_position_items(int move) +{ + if (skin->keyboard.offset) { + if (skin->background != NULL) { + skin->background->posx += move * skin->keyboard.offset; + } + skin->es->posx += move * skin->keyboard.offset; + + SkinButton* button = skin->buttons; + while (button) { + button->image.posx += move * skin->keyboard.offset; + button->key.posx += move * skin->keyboard.offset; + button = button->next; + } + } +} + +static void skin_animate_keyboard(int phase) +{ + //printf(">> skin_animate_keyboard, phase=%d\n", phase); + if (skin->keyboard.button) { + switch (skin->keyboard.button->key.state) { + // Keyboard opening + case ESkinBtn_Active: + case ESkinBtn_ActiveHighlighted: + { + if (phase == ANIM_NOT_USED) { + // No animation, just draw the full keyboard + //printf("skin_animate_keyboard, no animation, draw keyboard\n"); + SkinArea area = { skin->keyboard.image->posx, + skin->keyboard.image->posy, + skin->keyboard.image->width, + skin->keyboard.image->height }; + skin_draw_image(skin, + skin->keyboard.image, + &area); + } + if (phase == 0) { + // Resize the screen to fit keyboard + //printf("skin_animate_keyboard, expand screen\n"); + keyboard_animation_phase++; + skin_cleartooltip(NULL); + skin_position_items(1); + original_qemu_console_resize(skin->ds, skin->keyboard.screenwidth, + skin->keyboard.screenheight); + skin_handle_zooming(); + break; + } + + if (phase > 2 && phase < 7) { + // Draw actual animation phases + //printf("skin_animate_keyboard, opening, phase=%d\n", phase); + skin_draw_animated_keyboard(skin, skin->keyboard.image, 7-phase); + dpy_update(skin->ds, skin->keyboard.image->posx, + skin->keyboard.image->posy, + skin->keyboard.image->width, + skin->keyboard.image->height); + } + else if (phase == 7) { + // Draw fully open keyboard + //printf("skin_animate_keyboard, open\n"); + keyboard_animation_phase = 8; + skin_draw_animated_keyboard(skin, skin->keyboard.image, 0); + dpy_update(skin->ds, skin->keyboard.image->posx, + skin->keyboard.image->posy, + skin->keyboard.image->width, + skin->keyboard.image->height); + } + } + break; + // Keyboard closing + case ESkinBtn_Idle: + case ESkinBtn_Highlighted: + { + // Draw actual animation phases + if (phase > 2 && phase < 7) { + skin_draw_animated_keyboard(skin, skin->keyboard.image, phase-2); + dpy_update(skin->ds, skin->keyboard.image->posx, + skin->keyboard.image->posy, + skin->keyboard.image->width, + skin->keyboard.image->height); + } + else if (phase == 7) { + // Shrink the screen, no keyboard + //printf("skin_animate_keyboard, shrink screen\n"); + keyboard_animation_phase = 8; + skin_cleartooltip(NULL); + skin_position_items(-1); + original_qemu_console_resize(skin->ds, skin->width, skin->height); + skin_handle_zooming(); + } + } + default: + // Do nothing + break; + } + } + //printf("skin_animate_keyboard >>\n"); +} + +static void skin_update_keyboard(void *opaque) +{ + if (!skin->keyboard.animated || no_keyboard_anim) { + // Animated keyboard is not used + keyboard_animation_phase = ANIM_NOT_USED; + no_keyboard_anim = 0; + } + + //printf("skin_update_keyboard, phase=%d\n", keyboard_animation_phase); + switch (keyboard_animation_phase) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + //printf("skin_update_keyboard, executing anim...\n"); + skin_animate_keyboard(keyboard_animation_phase); + keyboard_animation_phase++; + setup_keyboard_animation_timer(); + break; + case 9: + //printf("skin_update_keyboard, end...\n"); + keyboard_animation_phase = ANIM_NOT_USED; + break; + case ANIM_NOT_USED: + skin_animate_keyboard(keyboard_animation_phase); + break; + default: + // Do nothing + break; + } +} + +static void setup_keyboard_animation_timer(void) +{ + // Set up timer for keyboard animation + //printf("setup_keyboard_timer\n"); + if (keyboard_timer) qemu_del_timer(keyboard_timer); + // Create new timer and call skin_update_keyboard when it expires + keyboard_timer = qemu_new_timer_ns(rt_clock, skin_update_keyboard, NULL); + // Set expiration time to be 0.1 seconds + qemu_mod_timer(keyboard_timer, qemu_get_clock_ns(rt_clock) + 100); + +} + +static void skin_draw_skin(SkinArea* area) +{ + // Skinning draws in various layers, from bottom to top: + // background color - background image - keyboard - buttons + + // Set the background color + skin_fill_background(skin, area); + // Draw the background image + if (skin->background && + skin_overlaps(skin->background, area)) { + skin_draw_image(skin, skin->background, area); + } + // Draw the keyboard + if (skin->keyboard.image && skin->keyboard.keys && !first_keyboard_check) { + skin_update_keyboard(NULL); + } + // Draw the buttons + SkinButton* button = skin->buttons; + while (button) { + if (skin_overlaps(&button->image, area)) { + // Check the switch state when redrawing the skin. Check the + // keyboard switch only once, because it messes up the animated + // keyboard if pressed it open from computer keyboard. + if (button == skin->keyboard.button && first_keyboard_check) { + skin_button_checkswitch(skin, button); + first_keyboard_check = 0; + no_keyboard_anim = 1; + skin_update_keyboard(NULL); + } + else if (button != skin->keyboard.button) { + skin_button_checkswitch(skin, button); + } + skin_draw_button(skin, button, ESkinBtn_forceredraw); + } + button = button->next; + } +} + +#define RCT_BUFSIZE 256 +static void skin_rct_serve(void *opaque) +{ + struct sockaddr_in addr; + int i; + socklen_t addrlen = sizeof(addr); + int csock = qemu_accept(rct_sock, (struct sockaddr *)&addr, &addrlen); + if (csock >= 0) { + char req[RCT_BUFSIZE]; + char rep[RCT_BUFSIZE] = "ERROR:unidentified request"; + int rlen; + + // read and handle incoming RCT request + if ( (rlen = recv(csock, (void *)req, RCT_BUFSIZE, 0)) > 0) { + for(i=0;irotation ? "on" : "off"); + } else if (strncmp(req, "setzoom", 7) == 0) { + char *endp; + int arg, newzoom, ok = 0; + + printf("char 8 %c\n", req[7]); + if (req[7] == '\n') { + sprintf(rep, "ERROR:missing setzoom argument"); + } else if (req[7] == '=') { + arg = strtol(req + 8, &endp, 10); + newzoom = arg; + ok = 1; + } else if ((req[7] == '+' || req[7] == '-') && req[8] == '=') { + // set relative to old value + arg = strtol(req + 9, &endp, 10); + if (!*endp && req[7] == '-') { + arg = -arg; + } + newzoom = zoom_factor + arg; + ok = 1; + } + + if (ok) { + if (*endp) { + sprintf(rep, "ERROR:invalid setzoom argument:%s", + req + ((req[7] == '=') ? 8 : 9)); + } else if (newzoom < ZOOM_MIN_FACTOR || + newzoom > ZOOM_MAX_FACTOR) { + sprintf(rep, "ERROR:new zoom factor out of range:%d", + newzoom); + } else { + zoom_factor = newzoom; + skin_handle_zooming(); + sprintf(rep, "OK:%d", zoom_factor); + } + } // otherwise the default error message applies + } else if (strncmp(req, "setrotation", 11) == 0) { + if (req[11] == '\n') { + // toggle current state if no argument + skin->rotation_req = (skin->rotation == off) ? on : off; + skin_handle_rotation(); + sprintf(rep, "OK:%s", + (skin->rotation == on) ? "on" : "off"); + } else if (strncmp(req + 11, "=on", 3) == 0) { + skin->rotation_req = on; + skin_handle_rotation(); + sprintf(rep, "OK:on"); + } else if (strncmp(req + 11, "=off", 4) == 0) { + skin->rotation_req = off; + skin_handle_rotation(); + sprintf(rep, "OK:off"); + } else if (req[11] == '=') { + sprintf(rep, "ERROR:invalid setrotation argument\n"); + } // otherwise the default error message applies + } + + if (send(csock, (const void *)rep, strlen(rep) + 1, 0) < 0) { + fprintf(stderr, "%s: send(): %s\n", + __FUNCTION__, strerror(socket_error())); + } + } + + closesocket(csock); + } else { + fprintf(stderr, "%s: qemu_accept(): %s\n", + __FUNCTION__, strerror(socket_error())); + } +} + +static int skin_rct_initialize(int rctport) +{ + struct sockaddr_in addr; + + if ( (rct_sock = qemu_socket(PF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "%s: qemu_socket(): %s\n", + __FUNCTION__, strerror(socket_error())); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(rctport); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(rct_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fprintf(stderr, "%s: bind(): %s\n", __FUNCTION__, strerror(socket_error())); + return -1; + } + + if (listen(rct_sock, 1) < 0) { + fprintf(stderr, "%s: listen(): %s\n", __FUNCTION__, strerror(socket_error())); + return -1; + } + + return qemu_set_fd_handler(rct_sock, skin_rct_serve, NULL, NULL); +} + +int skinning_init(char* skin_file, int portrait, int rctport) +{ + skin = skin_load_configuration(skin_file, portrait); + + if (skin) { + if (skin->rotation == on && skin->keyboard.image && + skin->keyboard.keys && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + skin_position_items(1); + } + if (rctport) { + skin_rct_initialize(rctport); + } + } + return (skin == NULL); +} + +static void skin_host_update(DisplayState *ds, int x, int y, int w, int h) +{ + //printf("skin_host_update()\n"); + // We probably initiated this +} + +static void skin_host_setdata(DisplayState *ds) +{ + //printf("skin_host_setdata\n"); +} + +static void skin_host_resize(DisplayState *ds) +{ + if (!is_graphic_console()) return; + if (startup) { + int width = 0, height = 0; + dpy_getresolution(ds, &width, &height); + //printf("%s, dpy_getresolution: width=%d, height=%d, skin->width=%d, skin->height=%d\n", __FUNCTION__, width, height, skin->width, skin->height); + if (width != 0 && height != 0) { + startup = 0; +#if 0 + if (skin->width >= width || + skin->height >= height ) { + // Determine zoom level + while( (skin->width * zoom_factor / 100 >= width || + skin->height * zoom_factor / 100 >= height) && + zoom_factor > ZOOM_MIN_FACTOR) + zoom_factor -= ZOOM_STEP; + skin_handle_zooming(); + } +#endif + } + } + SkinArea area = { 0, 0, ds_get_width(skin->ds), ds_get_height(skin->ds) }; + skin_draw_skin(&area); + // Likely we got a new buffer, update the emulated display buffer + if (skin->es && skin->es->ds) { + skin->es->ds->surface = qemu_resize_displaysurface(skin->es->ds, + skin->es->width, skin->es->height); + } + // Redraw the emulated screen on top + vga_hw_invalidate(); + vga_hw_update(); + + dpy_update(skin->ds, 0, 0, ds_get_width(skin->ds), ds_get_height(skin->ds)); +} + +static void skin_update(DisplayState *ds, int x, int y, int w, int h) +{ + // Updates done by emulated screen need to be converted to our display + int xd = x, yd = y, wd = w, hd = h; + if (skin->rotation == off) { + // Check if the drawing will fit the screen + if (ds_get_width(skin->ds) >= (w + (x + skin->es->posx)) && + ds_get_height(skin->ds) >= (h + (y + skin->es->posy)) ) { + xd += skin->es->posx; + yd += skin->es->posy; + } + } + else { + skin_rotate_buffer(skin, ds, x, y, w, h); + xd = skin->es->posx + skin->es->height - (y + h - 1); + yd = skin->es->posy + x; + wd = h; + hd = w; + } + // Check if we have an overlapping tooltip + if (skin->tooltip.image) { + struct SkinArea clip = { xd, yd, wd, hd }; + if (skin_overlaps(skin->tooltip.image, &clip)) { + SkinArea drawclip = skin_cliparea(skin->tooltip.image, &clip); + skin_draw_tooltip(&drawclip); + } + } + // Update the correct part + dpy_update(skin->ds, xd, yd, wd, hd); +} + +static void skin_setdata(DisplayState *ds) +{ + //printf("skin_setdata()\n"); + //if (skin->es && skin->es->ds) + // dpy_setdata(skin->es->ds); + if (!es_ds) es_ds = ds; + dpy_setdata(skin->ds); +} + +static void skin_resize(DisplayState *ds) +{ + // Emulated display has resized + //printf("skin_resize()\n"); + // Resize our display also then... + //qemu_console_resize(skin->ds, skin->width, skin->height); + skin_setdata(ds); +} + +static void skin_refresh(DisplayState *ds) +{ + //printf("skin_refresh()\n"); +} + +static DisplaySurface* skin_create_displaysurface(int width, int height) +{ + //printf(">> skin_create_displaysurface(%d,%d)\n", width, height); + // We need a host surface to do this + if (!skin) return NULL; + DisplaySurface *surface = + (DisplaySurface*)qemu_mallocz(sizeof(DisplaySurface)); + surface->width = width; + surface->height = height; + surface->linesize = ds_get_linesize(skin->ds); + surface->pf = skin->ds->surface->pf; + if (skin->rotation == off) { + // No rotation, draw directly to display buffer + surface->data = ds_get_data(skin->ds) + + skin->es->posy * ds_get_linesize(skin->ds) + + skin->es->posx * ds_get_bytes_per_pixel(skin->ds); + } + else { + surface->linesize = ds_get_bytes_per_pixel(skin->ds) * surface->width; + // Rotation is done in skinning source with skin_rotate_buffer + surface->data = qemu_malloc(height * surface->linesize); + // Mark data is allocated here + surface->flags |= QEMU_ALLOCATED_FLAG; + } + return surface; +} + +static void skin_free_displaysurface(DisplaySurface *surface) +{ + //printf("skin_free_displaysurface\n"); + if (surface == NULL) + return; + + if (surface->flags & QEMU_ALLOCATED_FLAG) + qemu_free(surface->data); + qemu_free(surface); +} + +static DisplaySurface* skin_resize_displaysurface(DisplaySurface *surface, int width, int height) +{ + //printf("skin_resize_displaysurface, width=%d, height=%d\n", width, height); + skin_free_displaysurface(surface); + return skin_create_displaysurface(width, height); +} + +int is_skin_available(void); +int is_skin_available(void) +{ + return (skin != NULL); +} + +static void skin_handle_zooming(void) +{ + //printf("skin_handle_zooming, zoom_factor=%d%%\n", zoom_factor); + + if (skin->keyboard.button && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + dpy_enablezoom(skin->ds, skin->keyboard.screenwidth * zoom_factor / 100, + skin->keyboard.screenheight * zoom_factor / 100); + } + else { + // No keyboard needs to be drawn + dpy_enablezoom(skin->ds, skin->width * zoom_factor / 100, + skin->height * zoom_factor / 100); + } +} + +static void skin_show_zooming_level(int zoom_level, int posx) +{ + // Create the correct text for the zooming level + SkinTooltip* tooltip = &skin->tooltip; + char text[20]; + sprintf(text, "Zooming to %d%%", zoom_level); + SkinImage *image = skin_image_createtext(skin, text); + if (image) { + // Everything set, store the image and have it drawn + tooltip->image = image; + image->posx = posx - image->width - ZOOM_INDICATOR_POS; + image->posy = ZOOM_INDICATOR_POS; + struct SkinArea clip = { image->posx, image->posy, + image->width, image->height }; + // Make a "dummy" timer + if (!skin->tooltip.timer) { + skin->tooltip.timer = qemu_new_timer_ns(rt_clock, + skin_cleartooltip, + NULL); + } + // Prevent zoom button's own tooltip appearing. Set long expiration time. + qemu_mod_timer(skin->tooltip.timer, qemu_get_clock_ns(rt_clock) + 600000000); // 1000 min + // Draw the zooming level indicator + skin_draw_tooltip(&clip); + dpy_update(skin->ds, image->posx, image->posy, + image->width, image->height); + } +} + +static void skin_key_handler(void *opaque, int keycode) +{ + // Check if someone triggered one of our buttons + int keyvalue = keycode & 0x7F; + int released = keycode & 0x80; + SkinButton* button = skin->buttons; + while (button) { + if ((button->key.keycode & 0x7F) == keyvalue) { + int state = skin_button_handle_key(&button->key, !released); + if (skin_draw_button(skin, button, state)) { + dpy_update(skin->ds, button->image.posx, button->image.posy, + button->image.width, button->image.height); + } + // Check if the keyboard needs to be udpated + if (button == skin->keyboard.button && released) { + if (skin->keyboard.animated) { + keyboard_animation_phase = 0; + skin_update_keyboard(NULL); + } + else { + // Changing the size, automatically triggers a redraw + if (button->key.state == ESkinBtn_Active || + button->key.state == ESkinBtn_ActiveHighlighted) { + original_qemu_console_resize(skin->ds, skin->keyboard.screenwidth, + skin->keyboard.screenheight); + SkinArea area = { skin->keyboard.image->posx, + skin->keyboard.image->posy, + skin->keyboard.image->width, + skin->keyboard.image->height }; + skin_handle_zooming(); + skin_draw_image(skin, + skin->keyboard.image, + &area); + } + else { + original_qemu_console_resize(skin->ds, skin->width, skin->height); + skin_handle_zooming(); + } + } + } + // Check if rotation button is pressed (test-code) + if (button->key.keycode == 67 && released) { + if (skin->rotation_req == on) skin->rotation_req = off; + else skin->rotation_req = on; + } + + // Zoom buttons + if (button->key.keycode == 87 && released) { // F11 + // Zooming in, zoom_factor in % + if (zoom_factor < ZOOM_MAX_FACTOR) { + zoom_factor += ZOOM_STEP; + no_keyboard_anim = 1; + skin_handle_zooming(); + skin_show_zooming_level(zoom_factor, button->image.posx); + } + } + if (button->key.keycode == 88 && released) { // F12 + // Zooming out, zoom_factor in % + if (zoom_factor > ZOOM_MIN_FACTOR) { + zoom_factor -= ZOOM_STEP; + no_keyboard_anim = 1; + skin_handle_zooming(); + skin_show_zooming_level(zoom_factor, button->image.posx); + } + } + } + button = button->next; + } + if (skin->keyboard.button && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + SkinKey* key = skin->keyboard.keys; + while (key) { + if ((key->keycode & 0x7F) == keyvalue) { + int state = skin_button_handle_key(key, !released); + if (skin_highlight_key(skin, key, state)) { + dpy_update(skin->ds, key->posx, key->posy, + key->width, key->height); + } + } + key = key->next; + } + } + + if (skin->mouse_event == off) skin_handle_rotation(); +} + +DisplayState *graphic_console_init(vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + vga_hw_text_update_ptr text_update, + void *opaque) +{ + DisplayState *ds; + //printf(">> graphic_console_init\n"); + // Host display for skin, register it to the qemu system + ds = qemu_graphic_console_init(update, invalidate, screen_dump, text_update, opaque); + + // If skinning is enabled, we create a host display, otherwise return ds + if (is_skin_available() && ds) { + // We need to know when we are resized + DisplayChangeListener *dcl; + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); + dcl->dpy_update = skin_host_update; + dcl->dpy_setdata = skin_host_setdata; + dcl->dpy_resize = skin_host_resize; + dcl->dpy_refresh = NULL; + register_displaychangelistener(ds, dcl); + // Store the DisplayState to the SkinScreen information + skin->ds = ds; + // Resize the display to our own size + if (skin->keyboard.button && + (skin->keyboard.button->key.defaultstate == on || + skin->keyboard.button->key.defaultstate == undefined)) { + original_qemu_console_resize(skin->ds, skin->keyboard.screenwidth, + skin->keyboard.screenheight); + } else { + //printf("skin->ds=%x, skin->width=%d, skin->height=%d\n", skin->ds, skin->width, skin->height); + original_qemu_console_resize(skin->ds, skin->width, skin->height); + } + + // Create a new DisplayState, this is the emulated display, we keep it to ourselves + ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState)); + // Also we want control over the emulated display ourselves + DisplayAllocator *da = qemu_mallocz(sizeof(DisplayAllocator)); + da->create_displaysurface = skin_create_displaysurface; + da->resize_displaysurface = skin_resize_displaysurface; + da->free_displaysurface = skin_free_displaysurface; + ds->allocator = da; + //printf("%s, es->width=%d, es->height=%d\n", __FUNCTION__, skin->es->width, skin->es->height); + ds->surface = skin_create_displaysurface(skin->es->width, skin->es->height); + // Client DisplayState, which is the actual screen we are emulating + skin->es->ds = ds; + + //Reserve es posx & posy for SDL blit + es_posx = skin->es->posx; + es_posy = skin->es->posy; + // We want all events on our emulated screen + DisplayChangeListener *skindcl = qemu_mallocz(sizeof(DisplayChangeListener)); + skindcl->dpy_update = skin_update; + skindcl->dpy_setdata = skin_setdata; + skindcl->dpy_resize = skin_resize; + skindcl->dpy_refresh = skin_refresh; + register_displaychangelistener(ds, skindcl); + + // We are also interested in keyboard events to match possible switches + qemu_add_kbd_event_handler(skin_key_handler, NULL); + //printf("graphic_console_init >>\n"); + } + return ds; +} + +void skin_toggle_full_screen(DisplayState *ds) +{ + skin_update_keyboard(NULL); + skin_handle_zooming(); +} + +void qemu_console_resize(DisplayState *ds, int width, int height) +{ + //printf(">> skinning: qemu_console_resize, width=%d, height=%d\n", width, height); + if (is_skin_available() && skin->ds && skin->es && skin->es->ds == ds) { + int neww = skin->width; + int newh = skin->height; + if (skin->keyboard.button && + (skin->keyboard.button->key.state == ESkinBtn_Active || + skin->keyboard.button->key.state == ESkinBtn_ActiveHighlighted)) { + neww = skin->keyboard.screenwidth; + newh = skin->keyboard.screenheight; + } + // Record the required width / height + skin->es->width = width; + skin->es->height = height; + if (ds_get_width(skin->ds) == neww && + ds_get_height(skin->ds) == newh ) { + // Just change the emulated screen size + skin->es->ds->surface = qemu_resize_displaysurface(skin->es->ds, + skin->es->width, skin->es->height); + } + else { + original_qemu_console_resize(skin->ds, neww, newh); + skin_handle_zooming(); + } + } + else + original_qemu_console_resize(ds, width, height); + //printf("skinning: qemu_console_resize >>\n"); +} + + +QEMUPutMouseEntry *original_qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name); + +QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name) +{ + if (skin) { + // If a skin is applied, keep reference to the calling one + // but don't register that to qemu + QEMUPutMouseEntry *other = qemu_mallocz(sizeof(*other)); + other->qemu_put_mouse_event = func; + other->qemu_put_mouse_event_opaque = opaque; + other->qemu_put_mouse_event_absolute = absolute; + other->qemu_put_mouse_event_name = qemu_strdup(name); + original_qemu_add_mouse_event_handler(skin_mouse_event, other, 1, "Skin mouse handling"); + return other; + } + return original_qemu_add_mouse_event_handler(func, opaque, absolute, name); +} diff --git a/ui/sdl.c b/ui/sdl.c index dc5c3a1bc..09634b044 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -63,10 +63,32 @@ static SDL_PixelFormat host_format; static int scaling_active = 0; static Notifier mouse_mode_notifier; +#ifdef CONFIG_SKINNING +void skin_toggle_full_screen(DisplayState *ds); +static int host_display_width; +static int host_display_height; +extern DisplayState *es_ds; +extern int es_posx; +extern int es_posy; +#endif + static void sdl_update(DisplayState *ds, int x, int y, int w, int h) { // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); - SDL_Rect rec; + SDL_Rect rec, rec_src; + +#ifdef CONFIG_SKINNING + rec_src.x = x - es_posx; + rec_src.y = y - es_posy; + rec_src.w = w; + rec_src.h = h; +#else + rec_src.x = x; + rec_src.y = y; + rec_src.w = w; + rec_src.h = h; +#endif + rec.x = x; rec.y = y; rec.w = w; @@ -87,6 +109,12 @@ static void sdl_update(DisplayState *ds, int x, int y, int w, int h) static void sdl_setdata(DisplayState *ds) { + +#ifdef CONFIG_SKINNING + if (es_ds) + ds = es_ds; +#endif + if (guest_screen != NULL) SDL_FreeSurface(guest_screen); guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds), @@ -133,6 +161,32 @@ static void sdl_resize(DisplayState *ds) } } +#ifdef CONFIG_SKINNING +static void sdl_scale_window(DisplayState *ds, int width, int height) +{ + // Alter window size by zooming skin images in or out + if (real_screen) { + int bpp = real_screen->format->BitsPerPixel; + if (bpp != 16 && bpp != 32) + bpp = 32; + do_sdl_resize(width, height, bpp); + scaling_active = 1; + if (!is_buffer_shared(ds->surface)) { + ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds)); + dpy_resize(ds); + } + vga_hw_invalidate(); + vga_hw_update(); + } +} + +static void sdl_getresolution(int *width, int *height) +{ + if (width) *width = host_display_width; + if (height) *height = host_display_height; +} +#endif + static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf) { PixelFormat qemu_pf; @@ -541,6 +595,9 @@ static void toggle_full_screen(DisplayState *ds) } vga_hw_invalidate(); vga_hw_update(); +#ifdef CONFIG_SKINNING + skin_toggle_full_screen(ds); +#endif } static void sdl_refresh(DisplayState *ds) @@ -845,6 +902,11 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) vi = SDL_GetVideoInfo(); host_format = *(vi->vfmt); +#ifdef CONFIG_SKINNING + host_display_width = vi->current_w; + host_display_height = vi->current_h; +#endif + /* Load a 32x32x4 image. White pixels are transparent. */ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); if (filename) { @@ -863,6 +925,10 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) dcl->dpy_refresh = sdl_refresh; dcl->dpy_setdata = sdl_setdata; dcl->dpy_fill = sdl_fill; +#ifdef CONFIG_SKINNING + dcl->dpy_enablezoom = sdl_scale_window; + dcl->dpy_getresolution = sdl_getresolution; +#endif ds->mouse_set = sdl_mouse_warp; ds->cursor_define = sdl_mouse_define; register_displaychangelistener(ds, dcl); diff --git a/vl.c b/vl.c index de232b75e..eeab066b0 100644 --- a/vl.c +++ b/vl.c @@ -215,7 +215,7 @@ int fd_bootchk = 1; int no_reboot = 0; int no_shutdown = 0; int cursor_hide = 1; -int graphic_rotate = 0; +int graphic_rotate = 1; uint8_t irq0override = 1; const char *watchdog; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; @@ -251,6 +251,12 @@ uint8_t qemu_uuid[16]; static QEMUBootSetHandler *boot_set_handler; static void *boot_set_opaque; +#ifdef CONFIG_SKINNING +int skinning_init(char* skin_file, int portrait, int rctport); +const char *skin_file = NULL; +int rctport = 0; +#endif /* CONFIG_SKINNING */ + static NotifierList exit_notifiers = NOTIFIER_LIST_INITIALIZER(exit_notifiers); @@ -2197,6 +2203,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_portrait: graphic_rotate = 1; break; + case QEMU_OPTION_landscape: + graphic_rotate = 0; + break; case QEMU_OPTION_kernel: kernel_filename = optarg; break; @@ -2551,6 +2560,21 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_full_screen: full_screen = 1; break; +#ifdef CONFIG_SKINNING + case QEMU_OPTION_skin: + skin_file = optarg; + printf("skin_file: %s\n", skin_file); + break; + case QEMU_OPTION_rctport: + { + const char *p; + p = optarg; + rctport = strtol(p, (char **)&p, 10); + if (p == optarg) + printf("Bad argument to rctport\n"); + break; + } +#endif #ifdef CONFIG_SDL case QEMU_OPTION_no_frame: no_frame = 1; @@ -2817,6 +2841,13 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "warning: unable to initialize simple trace backend\n"); } +#ifdef CONFIG_SKINNING + if( skin_file && skinning_init((char*)skin_file, graphic_rotate, rctport) ) { + fprintf( stderr, "Skin could not be initialised\n"); + exit(1); + } +#endif + /* If no data_dir is specified then try to find it relative to the executable path. */ if (!data_dir) {