diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/qemu-symbian-svp/gui.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/qemu-symbian-svp/gui.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,1732 @@ +/* + * GUI implementation + * + * Copyright (c) 2009 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "sysemu.h" +#include "stdio.h" +#include "qemu-timer.h" +#include "hw/gui.h" +#include "gui_host.h" +#include "gui_common.h" +#include "gui_parser.h" +#define HOST_ONLY_DEFS /* DFG: FIXME */ +#include "hw/fb_render_engine.h" +#include "gui_png.h" + +#define MAX_CONSOLES 12 + +typedef void (*button_action_t)(void *parameter); + +struct button_actions_s +{ + button_action_t down; + button_action_t up; +}; + +enum button_actions_e +{ + BUTTON_ACTION_NOACTION = 0, + BUTTON_ACTION_SENDKEY = 1, +}; + +typedef enum +{ + CA_POINTERAREA, + CA_BUTTON +} clickarea_type; + +typedef struct +{ + devid_t devid; + int absolute; + int grab_on_click; + int show_cursor_while_grabbing; + QEMUPutMouseEvent *callback; + void *opaque; +} pointer_area_t; + +typedef struct +{ + ImageID pressed_img_id; + enum button_actions_e actions; + void* parameter; +} button_t; + +typedef struct +{ + gui_area_t area; + clickarea_type type; + int id; +} clickable_area_t; + +typedef struct +{ + /* TODO: Optimize with a geometrical index */ + clickable_area_t *areas; + unsigned int n_areas; + clickable_area_t *currently_grabbing; /* NULL if none */ +} clickable_map_t; + +typedef struct +{ + devid_t devid; + DisplayState ds; + 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; +} ds_data_t; + +typedef struct +{ + ds_data_t *ds_data; + unsigned int n_ds; + + int order_priority; /* This is used to place this VT in the key sequence (i.e. skinned VTs first) */ + + pointer_area_t *pointerareas; + unsigned int n_pointerareas; + + KeyCallback key_callback; + void *key_opaque; + + /* skin data */ + int has_skin; + + /* If has skin, the following data applies. + Maybe all should be moved to skin_data_t. + */ + DisplayState gui_ds; /* just for skin and host images */ + + + void *background_buffer; + unsigned int background_row_size; + render_data *rdata; + int full_update; + + gui_image_t *images; + unsigned int n_images; + clickable_map_t clickable_map; + button_t *buttons; + unsigned int n_buttons; + +} vt_t; + +enum gui_input_state_t +{ + INPUT_STATE_GUI_UNLOADED, /* Compatible with old (gui-less) machines */ + INPUT_STATE_GUI_LOADED +}; + +typedef struct +{ + void (*gui_notify_toggle_fullscreen)(void); + void (*gui_notify_toggle_grabmode)(void); + void (*gui_notify_mouse_motion)(int dx, int dy, int dz, int x, int y, int state); + void (*gui_notify_mouse_button)(int dz, int x, int y, int state); + void (*gui_notify_mouse_warp)(int x, int y, int on); + void (*gui_notify_new_guest_cursor)(void); + void (*gui_notify_input_focus_lost)(void); + void (*gui_notify_app_focus)(int gain); +} gui_input_table_t; + +struct gui_data_t +{ + gui_host_callbacks_t host_callbacks; + vt_t vts[MAX_CONSOLES]; + unsigned int n_vts; + vt_t *current_vt; + VtID ordered_vts[MAX_CONSOLES]; /* Vts sorted by order_priority */ + screen_data_t screen_data; + + struct QEMUTimer *gui_timer; + uint64_t gui_timer_interval; + int idle; /* there is nothing to update (window invisible), set by vnc/sdl */ + + int updating; /* to prevent recursion */ + + /* Input stuff */ + KeyCallback dev_key_callback; + void *dev_key_opaque; + + int gui_fullscreen; + + const gui_input_table_t* input_table; + int guest_cursor; + int guest_x; + int guest_y; + int last_vm_running; + struct { + int absolute_enabled; + int gui_grab; /* if true, all keyboard/mouse events are grabbed */ + int gui_saved_grab; + int gui_fullscreen_initial_grab; + } unloaded_state_data; + + int loaded; +}; + +static struct gui_data_t* gui_data = NULL; +/*******************************************/ + + +/* Internal Functions ==================== */ +static parse_result_t load_gui_xml(const char* xml_file); +static void index_map(clickable_map_t *map); +static void initialize_display_areas(VtID vtid); +static void destroy_clickable_map(clickable_map_t *map); +static void destroy_images(VtID vtid); +static void gui_update_guest_display(vt_t *vt); +static void init_background(VtID vtid); +static void gui_set_input_state(enum gui_input_state_t input_state); +static void gui_update_caption(void); +static void gui_update_ds_data(DisplayState *ds, int in_skinned_vt); + +static void gui_loaded_grab_end(void); +static void gui_update_timer(int64_t ticks); + +/* ======================================= */ + +int ds_get_width(const DisplayState *ds) +{ + return ds->width; +} + +int ds_get_height(const DisplayState *ds) +{ + return ds->height; +} + +int ds_get_bits_per_pixel(const DisplayState *ds) +{ + return ds->depth; +} + +int ds_get_bgr(const DisplayState *ds) +{ + return ds->bgr; +} + +int ds_get_linesize(const DisplayState *ds) +{ + return ds->linesize; +} + +uint8_t *ds_get_data(const DisplayState *ds) +{ + return ds->data; +} + +static inline int vt_enabled(void) +{ + return gui_data->current_vt != NULL; +} + +static inline VtID current_vt_id(void) +{ + return gui_data->current_vt - &gui_data->vts[0]; +} + +static inline int is_current_vt(VtID vtid) +{ + return gui_data->current_vt == &gui_data->vts[vtid]; +} + +static inline void invalidate_ds(const ds_data_t* ds_data) +{ + if (ds_data->invalidate != NULL) + ds_data->invalidate(ds_data->opaque); +} + +static inline void update_ds(const ds_data_t* ds_data) +{ + if (ds_data->update != NULL) + ds_data->update(ds_data->opaque); +} + +static inline int get_vt_width(const vt_t *vt) +{ + if (vt->has_skin) + return GET_GUI_AREA_WIDTH(&vt->images[DEF_BACKGROUND_IMAGE].area); + else + return vt->ds_data[0].ds.width; +} + +static inline int get_vt_height(const vt_t *vt) +{ + if (vt->has_skin) + return GET_GUI_AREA_HEIGHT(&vt->images[DEF_BACKGROUND_IMAGE].area); + else + return vt->ds_data[0].ds.height; +} + +void dpy_update(DisplayState *s, int x, int y, int w, int h) +{ + s->dpy_update(s, x, y, w, h); +} + +void dpy_cursor(DisplayState *s, int x, int y) +{ + if (s->dpy_text_cursor) + s->dpy_text_cursor(s, x, y); +} + +static int accepts_kbd(const vt_t *vt) +{ + return (vt->key_callback != NULL); +#if 0 + DFG FIXME: manage multiple text consoles + int accepts = 0; + + if (vt->n_ds_data > 0) { + int vtid = 0; + do { + accepts = (vt->ds_data[vtid++]->); + } while (!accepts && vtid < vt->n_ds_data); + } + + return accepts; +#endif +} + +void gui_init(gui_host_callbacks_t* callbacks) +{ + gui_data = (struct gui_data_t*)qemu_mallocz(sizeof(struct gui_data_t)); + + gui_set_input_state(INPUT_STATE_GUI_UNLOADED); + if (callbacks != NULL) { + gui_data->host_callbacks = *callbacks; + gui_data->host_callbacks.set_screen_size(640, 480, 0); + gui_data->host_callbacks.get_screen_data(&gui_data->screen_data); + } + + gui_update_caption(); +} + +void gui_destroy(void) +{ + if (gui_data != NULL) { + if (gui_data->loaded) + gui_unload(); + qemu_free(gui_data); + gui_data = NULL; + } +} + +int gui_load(const char *xml_file) +{ + parse_result_t res; + int vt; + + res = load_gui_xml(xml_file); + if (res == PARSE_OK) { + gui_data->loaded = 1; + + for (vt = 0; vt < gui_data->n_vts; vt++) { + index_map(&gui_data->vts[vt].clickable_map); + if (gui_data->vts[vt].has_skin) { + gui_data->vts[vt].rdata = create_render_data(); + init_background(vt); + gui_show_image(vt, DEF_BACKGROUND_IMAGE); + } + initialize_display_areas(vt); + } + gui_set_input_state(INPUT_STATE_GUI_LOADED); + gui_data->current_vt = &gui_data->vts[0]; + /*gui_notify_console_select(0); Done in vl.c */ + } else { + if (parsing_location_error() != NULL) + fprintf(stderr, "GUI parser error %d at %s\n", res, parsing_location_error()); + } + + return res == PARSE_OK; +} + +void gui_unload(void) +{ + int vt; + if (gui_data->loaded) { + for (vt = 0; vt < gui_data->n_vts; vt++) { + destroy_images(vt); + destroy_clickable_map(&gui_data->vts[vt].clickable_map); + destroy_render_data(gui_data->vts[vt].rdata); + qemu_free(gui_data->vts[vt].background_buffer); + qemu_free(gui_data->vts[vt].ds_data); + } + gui_set_input_state(INPUT_STATE_GUI_UNLOADED); + gui_data->loaded = 0; + } +} + +int gui_needs_timer(void) +{ + return (gui_data->host_callbacks.process_events != NULL); +} + +void gui_set_timer(struct QEMUTimer *timer) +{ + gui_data->gui_timer = timer; +} + +static void init_background(VtID vtid) +{ + const int background_width = GET_GUI_AREA_WIDTH(&gui_data->vts[vtid].images[DEF_BACKGROUND_IMAGE].area); + const int background_height = GET_GUI_AREA_HEIGHT(&gui_data->vts[vtid].images[DEF_BACKGROUND_IMAGE].area); + + gui_data->vts[vtid].gui_ds.width = background_width; + gui_data->vts[vtid].gui_ds.height = background_height; + gui_data->host_callbacks.init_ds(&gui_data->vts[vtid].gui_ds); + gui_update_ds_data(&gui_data->vts[vtid].gui_ds, 1); + + gui_data->vts[vtid].background_row_size = background_width * 4; + + gui_data->vts[vtid].background_buffer = (void*)qemu_malloc( + gui_data->vts[vtid].background_row_size * background_height); + + set_fb_base_from_host(gui_data->vts[vtid].rdata, + gui_data->vts[vtid].background_buffer); + + set_cols(gui_data->vts[vtid].rdata, background_width); + set_rows(gui_data->vts[vtid].rdata, background_height); + + /* FIXME: these data should be taken from the image_data, + rather than hardcode this here. */ + /*set_pixel_order(gui_data->rdata, PO_BE);*/ + set_byte_order(gui_data->vts[vtid].rdata, BO_LE); + set_color_order(gui_data->vts[vtid].rdata, CO_RGB); + set_src_bpp(gui_data->vts[vtid].rdata, BPP_SRC_32); + /* **** */ + + gui_update_guest_display(&gui_data->vts[vtid]); +} + +static void gui_update_guest_display(vt_t *vt) +{ + if (!nographic && vt->gui_ds.depth != 0) { + render(&vt->gui_ds, vt->rdata, vt->full_update); + vt->full_update = 0; + } +} + +enum what_buffer_t { + FROM_BG_BUFFER, + FROM_DEF_BG_IMAGE +}; + +static inline char* calc_bg_area_address(VtID vtid, const gui_area_t *area, enum what_buffer_t what_buffer) +{ + return (what_buffer == FROM_BG_BUFFER ? + (char*)gui_data->vts[vtid].background_buffer : + (char*)gui_data->vts[vtid].images[DEF_BACKGROUND_IMAGE].image) + + area->y0 * gui_data->vts[vtid].background_row_size + + area->x0 * 4; +} + +static void paint_rectangle(VtID vtid, gui_area_t *area, const void* source, int src_row_size) +{ + const int area_height = GET_GUI_AREA_HEIGHT(area); + const int area_row_size = src_row_size > 0 ? src_row_size : GET_GUI_AREA_WIDTH(area) * 4; + char* dest = calc_bg_area_address(vtid, area, FROM_BG_BUFFER); + const char* src = (const char*)source; + int y = 0; + + for (y=0; y < area_height; y++) { + memcpy(dest, src, area_row_size); + dest += gui_data->vts[vtid].background_row_size; + src += area_row_size; + } +} + +void gui_show_image(VtID vtid, ImageID id) +{ + if (id == DEF_BACKGROUND_IMAGE) { + /* background is the whole DS */ + memcpy(gui_data->vts[vtid].background_buffer, + gui_data->vts[vtid].images[id].image, + gui_data->vts[vtid].background_row_size * + GET_GUI_AREA_HEIGHT(&gui_data->vts[vtid].images[id].area)); + + } else { + paint_rectangle(vtid, + &gui_data->vts[vtid].images[id].area, + gui_data->vts[vtid].images[id].image, + 0); + } + + gui_data->vts[vtid].images[id].visible = 1; + + gui_data->vts[vtid].full_update = 1; /* DFG TODO: IMPROVE! */ +} + +void gui_hide_image(VtID vtid, ImageID id) +{ + /* restore the background there */ + + paint_rectangle(vtid, + &gui_data->vts[vtid].images[id].area, + calc_bg_area_address(vtid, + &gui_data->vts[vtid].images[id].area, + FROM_DEF_BG_IMAGE), + gui_data->vts[vtid].background_row_size); + + gui_data->vts[vtid].images[id].visible = 0; + + gui_data->vts[vtid].full_update = 1; /* DFG TODO: IMPROVE! */ +} + +int gui_register_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *devname) +{ + if (gui_data->loaded) { + int found = 0; + VtID vtid = 0; + int parea; + + while (!found && vtid < gui_data->n_vts) { + parea = 0; + while (!found && parea < gui_data->vts[vtid].n_pointerareas) { + found = (strcmp(gui_data->vts[vtid].pointerareas[parea].devid, devname) == 0); + if (!found) + parea++; + } + if (!found) + vtid++; + } + + if (found) { + gui_data->vts[vtid].pointerareas[parea].callback = func; + gui_data->vts[vtid].pointerareas[parea].absolute = absolute; + gui_data->vts[vtid].pointerareas[parea].opaque = opaque; + } else { + fprintf(stderr, "Warning: pointer device %s not accepted by the gui. Device ignored.\n", devname); + } + + return found; + } else { + qemu_add_mouse_event_handler(func, opaque, absolute, devname); + return 1; + } +} + +void gui_set_paint_callbacks(DisplayState *ds, + vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + void *opaque) +{ + ds_data_t * const ds_data = &gui_data->vts[ds->vtid].ds_data[ds->dispid]; + ds_data->update = update; + ds_data->invalidate = invalidate; + ds_data->screen_dump = screen_dump; + ds_data->opaque = opaque; +} + +DisplayState *gui_get_graphic_console(const char *devname, + vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + void *opaque) +{ + DisplayState *ret = NULL; + + if (gui_data->loaded && devname != NULL) { + int found = 0; + VtID vtid = 0; + DisplayID dispid; + + while (!found && vtid < gui_data->n_vts) { + dispid = 0; + while (!found && dispid < gui_data->vts[vtid].n_ds) { + found = (strcmp(gui_data->vts[vtid].ds_data[dispid].devid, devname) == 0); + if (!found) + dispid++; + } + if (!found) + vtid++; + } + + if (found) + ret = &gui_data->vts[vtid].ds_data[dispid].ds; + else + fprintf(stderr, "Warning: frame buffer '%s' not found in the GUI, assigning a skinless VT to it.\n", devname); + } + + if (ret == NULL) /* Allocate a new (skinless) VT */ + ret = gui_new_vt(SKINLESS_GRAPHIC_VT_PRIORITY_ORDER); + + if (ret != NULL) + gui_set_paint_callbacks(ret, update, invalidate, screen_dump, opaque); + + return ret; +} + +void gui_register_dev_key_callback(KeyCallback cb, void *opaque) +{ + gui_data->dev_key_callback = cb; + gui_data->dev_key_opaque = opaque; +} + +void gui_register_vt_key_callback(DisplayState *ds, KeyCallback cb, void *opaque) +{ + gui_data->vts[ds->vtid].key_callback = cb; + gui_data->vts[ds->vtid].key_opaque = opaque; +} + +/* only for skinless vts */ +int gui_resize_vt(DisplayState *ds, int width, int height) +{ + const VtID vtid = ds->vtid; + + if (!gui_data->vts[vtid].has_skin) { + ds_data_t * const ds_data = &gui_data->vts[vtid].ds_data[0]; /* assume ds->dispid == 0 */ + + if (is_current_vt(vtid)) { + gui_data->host_callbacks.set_screen_size(width, + height, + gui_data->gui_fullscreen); + + gui_data->host_callbacks.get_screen_data(&gui_data->screen_data); + gui_update_ds_data(&ds_data->ds, 0); + } + + ds_data->ds.width = width; + ds_data->ds.height = height; + + if (!gui_data->updating) { + invalidate_ds(ds_data); + + if (is_current_vt(vtid)) + update_ds(ds_data); + } + + return 1; + } + else { + fprintf(stderr, "Error: cannot resize a skinned gui. Specify the dimensions in the XML file.\n"); + return 0; + } +} + +static VtID insert_new_vt(int order_priority) +{ + int position = 0; + VtID new_vtid; + + while(position < gui_data->n_vts && gui_data->vts[gui_data->ordered_vts[position]].order_priority <= order_priority) + position++; + + if (position < gui_data->n_vts) { + /* insert a place, shift all the remaining elements 1 position. */ + int i; + for (i = gui_data->n_vts; i > position; i--) + gui_data->ordered_vts[i] = gui_data->ordered_vts[i-1]; + } /* else : new one */ + + new_vtid = gui_data->n_vts++; + gui_data->ordered_vts[position] = new_vtid; + gui_data->vts[new_vtid].order_priority = order_priority; + + return new_vtid; +} + +DisplayState *gui_new_vt(int order_priority) +{ + int vtid = -1; + + if (gui_data->n_vts < MAX_CONSOLES-1) { + /* find where to insert this according to order_priority */ + vtid = insert_new_vt(order_priority); + + /* allocate and initialize */ + gui_data->vts[vtid].has_skin = 0; /* redundant, but to be clear */ + gui_data->vts[vtid].n_ds = 1; + gui_data->vts[vtid].ds_data = (ds_data_t*)qemu_mallocz(sizeof(ds_data_t)*1); + gui_data->host_callbacks.init_ds(&gui_data->vts[vtid].ds_data[0].ds); + gui_update_ds_data(&gui_data->vts[vtid].ds_data[0].ds, 0); + gui_data->vts[vtid].ds_data->ds.width = 640; + gui_data->vts[vtid].ds_data->ds.height = 480; + gui_data->vts[vtid].ds_data->ds.vtid = vtid; + gui_data->vts[vtid].ds_data->ds.dispid = 0; + return &gui_data->vts[vtid].ds_data->ds; + } else { + fprintf(stderr, "Error: too many consoles.\n"); + return NULL; + } +} + +/************ Button Actions **************/ +static void gui_button_noaction(void *parameter) +{ +} + +static void gui_button_sendkey_dn(void *parameter) +{ + const int key = (int)(long int)parameter; + + gui_notify_dev_key(key); +} + +static void gui_button_sendkey_up(void *parameter) +{ + const int key = (int)(long int)parameter; + + gui_notify_dev_key(key | 0x80); +} + +struct button_actions_s button_actions[] = +{ + { gui_button_noaction, gui_button_noaction }, + { gui_button_sendkey_dn, gui_button_sendkey_up }, +}; + +/************ Internal Functions Definition **************/ + +enum AreaAdjustment +{ + AREA_CONTAINED_OK, + AREA_NOT_CONTAINED, + AREA_DIMENSIONS_ADJUSTED +}; + +static enum AreaAdjustment adjust_area_to_bg(VtID vtid, gui_area_t *area) +{ + enum AreaAdjustment ret = AREA_CONTAINED_OK; + + const int bg_width = get_vt_width(&gui_data->vts[vtid]); + const int bg_height = get_vt_height(&gui_data->vts[vtid]); + + if (area->x0 > bg_width || area->y0 > bg_height) + ret = AREA_NOT_CONTAINED; + else { + const int area_width = GET_GUI_AREA_WIDTH(area); + const int area_height = GET_GUI_AREA_HEIGHT(area); + + if ((area->x0 + area_width) > bg_width || area_width <= 0) { + SET_GUI_AREA_WIDTH(area, bg_width - area->x0); + if (area_width > 0) + ret = AREA_DIMENSIONS_ADJUSTED; + } + + if ((area->y0 + area_height) > bg_height || area_height <= 0) { + SET_GUI_AREA_HEIGHT(area, bg_height - area->y0); + if (area_height > 0) + ret = AREA_DIMENSIONS_ADJUSTED; + } + } + + return ret; +} + +static void load_gui_image(VtID vtid, ImageID id, const image_node_t *image_node) +{ + gui_data->vts[vtid].images[id].area = image_node->area; + + if (id == DEF_BACKGROUND_IMAGE) { + /* Ensure x0,y0 are 0,0*/ + if (gui_data->vts[vtid].images[id].area.x0 != 0 || + gui_data->vts[vtid].images[id].area.y0 != 0) { + + fprintf(stderr, "Warning: invalid (x0,y0) for background. They should be (0,0).\n"); + gui_data->vts[vtid].images[id].area.x0 = 0; + gui_data->vts[vtid].images[id].area.y0 = 0; + } + } else { + /* if it's not the background, ensure it is contained in it */ + switch(adjust_area_to_bg(vtid, &gui_data->vts[vtid].images[id].area)) { + case AREA_CONTAINED_OK: + break; /* OK */ + case AREA_NOT_CONTAINED: + fprintf(stderr, "Warning: image id %d of vt %d outside background boundaries. Image ignored.\n", id, vtid); + break; + case AREA_DIMENSIONS_ADJUSTED: + fprintf(stderr, "Warning: image id %d of vt %d outside background boundaries. Image cropped.\n", id, vtid); + break; + } + } + + /* Support png only for now */ + gui_load_image_png(image_node->filename, &gui_data->vts[vtid].images[id]); +} + +static void load_gui_displayarea(VtID vtid, DisplayID id, displayarea_node_t *displayarea_node) +{ + if (adjust_area_to_bg(vtid, &displayarea_node->area) == AREA_CONTAINED_OK) { + DisplayState * const ds = &gui_data->vts[vtid].ds_data[id].ds; + ds->vtid = vtid; + ds->dispid = id; + ds->x0 = displayarea_node->area.x0; + ds->y0 = displayarea_node->area.y0; + ds->width = GET_GUI_AREA_WIDTH(&displayarea_node->area); + ds->height = GET_GUI_AREA_HEIGHT(&displayarea_node->area); + gui_data->host_callbacks.init_ds(ds); + gui_update_ds_data(ds, 1); + strcpy(gui_data->vts[vtid].ds_data[id].devid, displayarea_node->devid); + } else + fprintf(stderr, "Warning: displayarea id %d of vt %d outside background boundaries. Display ignored.", id, vtid); +} + +static void load_gui_button(VtID vtid, int nbutton, const button_node_t *button_node) +{ + const int area_id = gui_data->vts[vtid].clickable_map.n_areas++; + + /* fill button data */ + gui_data->vts[vtid].buttons[nbutton].pressed_img_id = button_node->pressed_img_id; + + if (button_node->action[0] == 0) { + gui_data->vts[vtid].buttons[nbutton].actions = BUTTON_ACTION_NOACTION; + } else if (strcmp(button_node->action, "sendkey") == 0) { + gui_data->vts[vtid].buttons[nbutton].actions = BUTTON_ACTION_SENDKEY; + gui_data->vts[vtid].buttons[nbutton].parameter = (void*)atol(button_node->parameter); + } else { + gui_data->vts[vtid].buttons[nbutton].actions = BUTTON_ACTION_NOACTION; + fprintf(stderr, "Warning: unknown button action '%s'\n", button_node->action); + } + + /* fill clickarea data */ + gui_data->vts[vtid].clickable_map.areas[area_id].area = button_node->area; + gui_data->vts[vtid].clickable_map.areas[area_id].type = CA_BUTTON; + gui_data->vts[vtid].clickable_map.areas[area_id].id = nbutton; +} + +static void load_gui_pointerarea(VtID vtid, int nparea, const pointerarea_node_t *parea_node) +{ + const int area_id = gui_data->vts[vtid].clickable_map.n_areas++; + + /* fill pointerarea data */ + strcpy(gui_data->vts[vtid].pointerareas[nparea].devid, parea_node->devid); + gui_data->vts[vtid].pointerareas[nparea].absolute = parea_node->absolute; + gui_data->vts[vtid].pointerareas[nparea].grab_on_click = parea_node->grab_on_click; + gui_data->vts[vtid].pointerareas[nparea].show_cursor_while_grabbing = parea_node->show_cursor_while_grabbing; + + /* fill clickarea data */ + gui_data->vts[vtid].clickable_map.areas[area_id].area = parea_node->area; + gui_data->vts[vtid].clickable_map.areas[area_id].type = CA_POINTERAREA; + gui_data->vts[vtid].clickable_map.areas[area_id].id = nparea; +} + +#define FLATTEN(type,first_node,function) \ + { \ + type *node = first_node; \ + type *next; \ + unsigned int i = 0; \ + while(node != NULL) { \ + function(i, node); \ + next = node->next; \ + qemu_free(node); \ + node = next; \ + i++; \ + } \ + } + +#define VT_FLATTEN(type,vtid,first_node,function) \ + { \ + type *node = first_node; \ + type *next; \ + unsigned int i = 0; \ + while(node != NULL) { \ + function(vtid, i, node); \ + next = node->next; \ + qemu_free(node); \ + node = next; \ + i++; \ + } \ + } + +#define ALLOC_ARRAY(type,n) (type*)qemu_mallocz((n)*sizeof(type)) + + +static void load_gui_vt(int nvt, const vt_node_t *vt_node) +{ + const VtID vt = insert_new_vt(SKINNED_VT_PRIORITY_ORDER); /* nvt + gui_data->n_vts;*/ + int n_clickareas = 0; + + gui_data->vts[vt].has_skin = 1; + + /* allocate memory first */ + gui_data->vts[vt].n_images = vt_node->n_images; + if (vt_node->n_images > 0) + gui_data->vts[vt].images = ALLOC_ARRAY(gui_image_t, vt_node->n_images); + + n_clickareas = vt_node->n_buttons + vt_node->n_pointerareas; + if (n_clickareas > 0) + gui_data->vts[vt].clickable_map.areas = ALLOC_ARRAY(clickable_area_t, n_clickareas); + gui_data->vts[vt].clickable_map.n_areas = 0; /* will be incremented while + loading buttons and pointerareas */ + + gui_data->vts[vt].n_buttons = vt_node->n_buttons; + if (vt_node->n_buttons > 0) + gui_data->vts[vt].buttons = ALLOC_ARRAY(button_t, vt_node->n_buttons); + + gui_data->vts[vt].n_pointerareas = vt_node->n_pointerareas; + if (vt_node->n_pointerareas > 0) + gui_data->vts[vt].pointerareas = ALLOC_ARRAY(pointer_area_t, vt_node->n_pointerareas); + + gui_data->vts[vt].n_ds = vt_node->n_displayareas; + if (vt_node->n_displayareas > 0) + gui_data->vts[vt].ds_data = ALLOC_ARRAY(ds_data_t, vt_node->n_displayareas); + + /* flatten subtrees */ + VT_FLATTEN(image_node_t, vt, vt_node->first_image_node, load_gui_image); + VT_FLATTEN(button_node_t, vt, vt_node->first_button_node, load_gui_button); + VT_FLATTEN(pointerarea_node_t, vt, vt_node->first_pointerarea_node, load_gui_pointerarea); + VT_FLATTEN(displayarea_node_t, vt, vt_node->first_displayarea_node, load_gui_displayarea); +} + +static parse_result_t load_gui_xml(const char* xml_file) +{ + gui_xml_tree_t gui_xml_tree; + parse_result_t ret; + + /* 1: parse the file */ + ret = parse_gui_xml(xml_file, &gui_xml_tree); + if (ret != PARSE_OK) + return ret; + + /* 2: allocate memory */ + if (gui_xml_tree.n_vts == 0) { + fprintf(stderr, "GUI Error: at least one VT shall be defined\n"); + return UNEXPECTED_EOF; + } + + FLATTEN(vt_node_t, gui_xml_tree.first_vt_node, load_gui_vt); + + return ret; +} + +#undef ALLOC_ARRAY +#undef FLATTEN +#undef VT_FLATTEN + +static void index_map(clickable_map_t *map) +{ + /* TODO: no geometrical index implemented yet */ +} + +static void initialize_display_areas(VtID vtid) +{ + int nds; + for(nds = 0; nds < gui_data->vts[vtid].n_ds; nds++) { + gui_data->host_callbacks.init_ds(&gui_data->vts[vtid].ds_data[nds].ds); + gui_update_ds_data(&gui_data->vts[vtid].ds_data[nds].ds, gui_data->vts[vtid].has_skin); + } +} + +static void destroy_images(VtID vtid) +{ + int i; + for (i=0; ivts[vtid].n_images; i++) + qemu_free(gui_data->vts[vtid].images[i].image); + + qemu_free(gui_data->vts[vtid].images); +} + +static void destroy_clickable_map(clickable_map_t *map) +{ + qemu_free(map->areas); +} + +static inline int area_contains(const gui_area_t *area, int x, int y) +{ +#define IN_RANGE(min,value,max) ((min)<=(value) && (value)<=(max)) + return IN_RANGE(area->x0, x, area->x1) && IN_RANGE(area->y0, y, area->y1); +#undef IN_RANGE +} + +/* TODO: Optimize with a geometrical index! */ +static clickable_area_t *find_clickarea(const vt_t *vt, int x, int y, AreaID *id_found) +{ + AreaID id = 0; + int found = 0; + clickable_area_t * carea; + + while (id < vt->clickable_map.n_areas && !found) { + carea = &vt->clickable_map.areas[id++]; + found = area_contains(&carea->area, x, y); + } + + if (found) { + *id_found = id-1; + return carea; + } else + return NULL; +} + +static inline int grabbing_mouse(void) +{ + if (vt_enabled()) + return gui_data->current_vt->clickable_map.currently_grabbing != NULL; + else + return 0; +} + +static inline int gui_grabbing(void) +{ + return grabbing_mouse() /*TODO when implementing VNC: || grabbing_kbd()*/; +} + +static pointer_area_t *current_parea(void) +{ + if (vt_enabled()) + if (gui_data->current_vt->clickable_map.currently_grabbing != NULL) + return &gui_data->current_vt->pointerareas[ + gui_data->current_vt->clickable_map.currently_grabbing->id + ]; + else + return NULL; + else + return NULL; +} + +/************************* Input Host Functions ****************************/ + +/* Wrappers and functions to common states */ + +static void gui_update_caption(void) +{ + if(gui_data->host_callbacks.set_caption != NULL) { + char buf[1024]; + const char *status = ""; + int grabbing; + + if (gui_data->loaded) + grabbing = (gui_data->current_vt->clickable_map.currently_grabbing != NULL); + else + grabbing = gui_data->unloaded_state_data.gui_grab; + + + if (!vm_running) + status = " [Stopped]"; + else if (grabbing) { + if (!alt_grab) + status = " - Press Ctrl-Alt to exit grab"; + else + status = " - Press Ctrl-Alt-Shift to exit grab"; + } + + if (qemu_name) + snprintf(buf, sizeof(buf), "QEMU (%s)%s", qemu_name, status); + else + snprintf(buf, sizeof(buf), "QEMU%s", status); + + gui_data->host_callbacks.set_caption(buf, "QEMU"); + } +} + +int gui_allows_fullscreen(void) +{ + return (gui_data->host_callbacks.set_screen_size != NULL); +} + +void gui_notify_toggle_fullscreen(void) +{ + gui_data->input_table->gui_notify_toggle_fullscreen(); +} + +void gui_notify_mouse_motion(int dx, int dy, int dz, int x, int y, int state) +{ + gui_data->input_table->gui_notify_mouse_motion(dx, dy, dz, x, y, state); +} + +void gui_notify_mouse_button(int dz, int x, int y, int state) +{ + gui_data->input_table->gui_notify_mouse_button(dz, x, y, state); +} + +void gui_notify_mouse_warp(int x, int y, int on) +{ + gui_data->input_table->gui_notify_mouse_warp(x, y, on); +} + +void gui_notify_term_key(int keysym) +{ + if (vt_enabled()) { + if (gui_data->current_vt->key_callback != NULL) + gui_data->current_vt->key_callback( + gui_data->current_vt->key_opaque, + keysym); + } +} + +void gui_notify_dev_key(int keysym) +{ + if (vt_enabled()) { + if (gui_data->dev_key_callback != NULL) + gui_data->dev_key_callback( + gui_data->dev_key_opaque, + keysym); + } +} + +static void gui_notify_console_select_by_vtid(VtID console) +{ + if (console < gui_data->n_vts) { + const int vt_width = get_vt_width(&gui_data->vts[console]); + const int vt_height = get_vt_height(&gui_data->vts[console]); + int disp; + gui_data->current_vt = &gui_data->vts[console]; + if (vt_width != gui_data->screen_data.width || + vt_height != gui_data->screen_data.height) { + + gui_data->host_callbacks.set_screen_size(vt_width, vt_height, gui_data->gui_fullscreen); + gui_data->host_callbacks.get_screen_data(&gui_data->screen_data); + } + + if (gui_data->vts[console].has_skin) + gui_update_ds_data(&gui_data->vts[console].gui_ds, 1); + gui_data->current_vt->full_update = 1; + + for (disp=0; disp < gui_data->current_vt->n_ds; disp++) { + gui_update_ds_data(&gui_data->current_vt->ds_data[disp].ds, gui_data->vts[console].has_skin); + if (gui_data->current_vt->ds_data[disp].invalidate != NULL) + gui_data->current_vt->ds_data[disp].invalidate( + gui_data->current_vt->ds_data[disp].opaque + ); + } + + /* determine kbd terminal mode */ + if (gui_data->host_callbacks.set_kbd_terminal_mode != NULL) + gui_data->host_callbacks.set_kbd_terminal_mode(gui_data->current_vt->key_callback != NULL); + } else + gui_data->current_vt = NULL; + +/* if (!gui_is_graphic_console()) { */ + if (gui_data->current_vt != NULL && accepts_kbd(gui_data->current_vt)) { + /* display grab if going to a text console */ + if (gui_grabbing()) + gui_loaded_grab_end(); + } + +} + +static VtID gui_get_console_order_vtid(int console_order) +{ + return gui_data->ordered_vts[console_order]; +} + +void gui_notify_console_select(int console_order) +{ + if (console_order < gui_data->n_vts) + gui_notify_console_select_by_vtid(gui_get_console_order_vtid(console_order)); +} + +void gui_notify_activate_display(DisplayState *ds) +{ + gui_notify_console_select_by_vtid(ds->vtid); +} + +void gui_notify_toggle_grabmode(void) +{ + gui_data->input_table->gui_notify_toggle_grabmode(); +} + +void gui_notify_new_guest_cursor(void) +{ + gui_data->input_table->gui_notify_new_guest_cursor(); +} + +void gui_notify_input_focus_lost(void) +{ + gui_data->input_table->gui_notify_input_focus_lost(); +} + +void gui_notify_update_tick(int64_t ticks) +{ + if (vt_enabled()) { + int disp; + + if (gui_data->current_vt->full_update) { + /* update the background */ + if (gui_data->current_vt->has_skin) + gui_update_guest_display(gui_data->current_vt); + + for (disp=0; disp < gui_data->current_vt->n_ds; disp++) + invalidate_ds(&gui_data->current_vt->ds_data[disp]); + } + + gui_data->updating = 1; + + for (disp=0; disp < gui_data->current_vt->n_ds; disp++) + update_ds(&gui_data->current_vt->ds_data[disp]); + + gui_data->updating = 0; + } + + gui_data->host_callbacks.process_events(); + + gui_update_timer(ticks); +} + +void gui_notify_app_focus(int gain) +{ + gui_data->input_table->gui_notify_app_focus(gain); +} + +void gui_notify_idle(int idle) +{ + gui_data->idle = idle; +} + +void gui_notify_repaint_screen(void) +{ + if (vt_enabled()) { + int disp; + + /* update the background */ + if (gui_data->current_vt->has_skin) { + dpy_update(&gui_data->current_vt->gui_ds, + 0, + 0, + gui_data->current_vt->gui_ds.width, + gui_data->current_vt->gui_ds.height + ); + } + + for (disp=0; disp < gui_data->current_vt->n_ds; disp++) { + dpy_update(&gui_data->current_vt->ds_data[disp].ds, + 0, + 0, + gui_data->current_vt->ds_data[disp].ds.width, + gui_data->current_vt->ds_data[disp].ds.height + ); + } + } +} + +void gui_notify_screen_dump(const char *filename) +{ + if (vt_enabled()) { + if (gui_data->current_vt->n_ds > 0 && + gui_data->current_vt->ds_data[0].screen_dump != NULL) { + gui_data->current_vt->ds_data[0].screen_dump( + gui_data->current_vt->ds_data[0].opaque, + filename); + } + } +} + +int gui_is_display_active(DisplayState *ds) +{ + return (gui_data->current_vt - gui_data->vts) == ds->vtid; +} + +void gui_refresh_caption(void) +{ + if (gui_data->last_vm_running != vm_running) { + gui_data->last_vm_running = vm_running; + gui_update_caption(); + } +} + +static void gui_update_timer(int64_t ticks) +{ + qemu_mod_timer(gui_data->gui_timer, + (gui_data->gui_timer_interval ? + gui_data->gui_timer_interval : + GUI_REFRESH_INTERVAL) + + ticks); +} + +static inline int col_to_bytes(int bpp, int col) +{ + switch(bpp) { + case 32: + return col * 4; + case 24: + return col * 3; + case 16: + case 15: + return col * 2; + case 8: + return col; + case 4: + return col >> 1; + case 2: + return col >> 2; + case 1: + return col >> 3; + default: + return 0; + } +} + +static void gui_update_ds_data(DisplayState *ds, int in_skinned_vt) +{ +#if 0 + DFG: TODO: see why this comes wrong + ds->linesize = gui_data->screen_data.linesize; +#endif + ds->depth = gui_data->screen_data.depth; + ds->bgr = gui_data->screen_data.bgr; + if (in_skinned_vt) { + ds->linesize = col_to_bytes(ds->depth, gui_data->screen_data.width); + /* calc offset */ + ds->data = gui_data->screen_data.data + ds->y0 * ds->linesize + col_to_bytes(ds->depth, ds->x0); + } else { + ds->linesize = gui_data->screen_data.linesize; + ds->data = gui_data->screen_data.data; + } + +} + +/* ************ GUI UNLOADED VERSION ********************/ + +static void gui_unloaded_hide_cursor(void) +{ + if (!cursor_hide) + return; + + if (kbd_mouse_is_absolute()) { + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_HIDDEN); + } else { + gui_data->host_callbacks.turn_cursor_off(); + } +} + +static void gui_unloaded_show_cursor(void) +{ + gui_cursor_type_t cursor_type; + + if (!cursor_hide) + return; + + if (!kbd_mouse_is_absolute()) { + if (gui_data->guest_cursor && + (gui_data->unloaded_state_data.gui_grab || + kbd_mouse_is_absolute() || + gui_data->unloaded_state_data.absolute_enabled)) + cursor_type = GUI_CURSOR_GUEST_SPRITE; + else + cursor_type = GUI_CURSOR_NORMAL; + + gui_data->host_callbacks.turn_cursor_on(cursor_type); + } +} + +static void gui_unloaded_grab_start(void) +{ + if (gui_data->guest_cursor) { + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); + gui_data->host_callbacks.mouse_warp(gui_data->guest_x, + gui_data->guest_y); + } else + gui_unloaded_hide_cursor(); + + gui_data->host_callbacks.grab_input_on(); + gui_data->unloaded_state_data.gui_grab = 1; + gui_update_caption(); +} + +static void gui_unloaded_grab_end(void) +{ + gui_data->host_callbacks.grab_input_off(); + gui_data->unloaded_state_data.gui_grab = 0; + gui_unloaded_show_cursor(); + gui_update_caption(); +} + +static void gui_unloaded_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) +{ + if (kbd_mouse_is_absolute()) { + if (!gui_data->unloaded_state_data.absolute_enabled) { + gui_unloaded_hide_cursor(); + if (gui_data->unloaded_state_data.gui_grab) { + gui_unloaded_grab_end(); + } + gui_data->unloaded_state_data.absolute_enabled = 1; + } + + dx = x * 0x7FFF / (gui_data->screen_data.width - 1); + dy = y * 0x7FFF / (gui_data->screen_data.height - 1); + } else if (gui_data->unloaded_state_data.absolute_enabled) { + gui_unloaded_show_cursor(); + gui_data->unloaded_state_data.absolute_enabled = 0; + } else if (gui_data->guest_cursor) { + /* calc relative coords + dx = x - guest_x + dy = y - guest_y + */ + x -= gui_data->guest_x; + y -= gui_data->guest_y; + gui_data->guest_x += x; + gui_data->guest_y += y; + dx = x; + dy = y; + } + + kbd_mouse_event(dx, dy, dz, state); +} + +static void gui_unloaded_notify_toggle_fullscreen(void) +{ + gui_data->gui_fullscreen = !gui_data->gui_fullscreen; + + if (gui_data->gui_fullscreen) { + gui_data->unloaded_state_data.gui_saved_grab = + gui_data->unloaded_state_data.gui_grab; + gui_unloaded_grab_start(); + } else { + if (!gui_data->unloaded_state_data.gui_saved_grab) + gui_unloaded_grab_end(); + } + + gui_data->host_callbacks.set_screen_size( + gui_data->screen_data.width, + gui_data->screen_data.height, + gui_data->gui_fullscreen); + + gui_data->host_callbacks.get_screen_data(&gui_data->screen_data); + + gui_notify_console_select_by_vtid(current_vt_id()); +} + +static void gui_unloaded_notify_mouse_motion(int dx, int dy, int dz, int x, int y, int state) +{ + if (gui_data->unloaded_state_data.gui_grab || + kbd_mouse_is_absolute() || + gui_data->unloaded_state_data.absolute_enabled) { + gui_unloaded_send_mouse_event(dx, dy, dz, x, y, state); + } +} + +static void gui_unloaded_notify_mouse_button(int dz, int x, int y, int state) +{ + if (gui_data->unloaded_state_data.gui_grab || kbd_mouse_is_absolute()) { + gui_unloaded_send_mouse_event(0, 0, dz, x, y, state); + } else { + if (state == MOUSE_EVENT_LBUTTON) { + /* start grabbing all events */ + gui_unloaded_grab_start(); + } + } +} + +static void gui_unloaded_notify_mouse_warp(int x, int y, int on) +{ + if (on) { + if (!gui_data->guest_cursor) + gui_unloaded_show_cursor(); + if (gui_data->unloaded_state_data.gui_grab || + kbd_mouse_is_absolute() || + gui_data->unloaded_state_data.absolute_enabled) { + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); + gui_data->host_callbacks.mouse_warp(gui_data->guest_x, + gui_data->guest_y); + } + } else if (gui_data->unloaded_state_data.gui_grab) + gui_unloaded_hide_cursor(); + gui_data->guest_cursor = on; + gui_data->guest_x = x; + gui_data->guest_y = y; +} + +static void gui_unloaded_notify_toggle_grabmode(void) +{ + if (!gui_data->unloaded_state_data.gui_grab) { + int app_active = 1; + + /* if the application is not active, + do not try to enter grab state. It + prevents + 'SDL_WM_GrabInput(SDL_GRAB_ON)' + from blocking all the application + (SDL bug). */ + if (gui_data->host_callbacks.is_app_active != NULL) + app_active = gui_data->host_callbacks.is_app_active(); + + if (app_active) + gui_unloaded_grab_start(); + } else { + gui_unloaded_grab_end(); + } +} + +static void gui_unloaded_notify_new_guest_cursor(void) +{ + /* DFG: DANGER, this call also invokes show_cursor() */ + if (gui_data->guest_cursor && + (gui_data->unloaded_state_data.gui_grab || + kbd_mouse_is_absolute() || + gui_data->unloaded_state_data.absolute_enabled)) + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); +} + +static void gui_unloaded_notify_input_focus_lost(void) +{ + if (gui_data->unloaded_state_data.gui_grab && + !gui_data->unloaded_state_data.gui_fullscreen_initial_grab) + gui_unloaded_grab_end(); +} + +static void gui_unloaded_notify_app_focus(int gain) +{ + if (gain) { + /* Back to default interval */ + gui_data->gui_timer_interval = 0; + gui_data->idle = 0; + } else { + /* Sleeping interval */ + gui_data->gui_timer_interval = 500; + gui_data->idle = 1; + } +} + +/******************* GUI LOADED VERSION *********************/ + +static void gui_loaded_hide_cursor(void) +{ + if (!cursor_hide) + return; + + gui_data->host_callbacks.turn_cursor_off(); +} + +static void gui_loaded_show_cursor(void) +{ + gui_cursor_type_t cursor_type; + + if (!cursor_hide) + return; + + if (gui_grabbing()) { + if (gui_data->guest_cursor && current_parea() != NULL && + current_parea()->show_cursor_while_grabbing) { + cursor_type = GUI_CURSOR_GUEST_SPRITE; + }else + cursor_type = GUI_CURSOR_NORMAL; + } else + cursor_type = GUI_CURSOR_NORMAL; + + gui_data->host_callbacks.turn_cursor_on(cursor_type); +} + +static void gui_loaded_grab_start(void) +{ + if (gui_data->guest_cursor) { + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); + gui_data->host_callbacks.mouse_warp(gui_data->guest_x, + gui_data->guest_y); + } else + gui_loaded_hide_cursor(); + + gui_data->host_callbacks.grab_input_on(); + gui_update_caption(); +} + +static void gui_loaded_grab_end(void) +{ + gui_data->host_callbacks.grab_input_off(); + gui_data->current_vt->clickable_map.currently_grabbing = NULL; + gui_loaded_show_cursor(); + gui_update_caption(); +} + +static void gui_loaded_send_mouse_event(clickable_area_t *carea, int dx, int dy, int dz, int x, int y, int state) +{ + pointer_area_t * const parea = &gui_data->current_vt->pointerareas[carea->id]; + + if (parea->absolute) { + dx = x * 0x7FFF / (GET_GUI_AREA_WIDTH(&carea->area)- 1); + dy = y * 0x7FFF / (GET_GUI_AREA_HEIGHT(&carea->area) - 1); + } else if (gui_data->guest_cursor) { + /* calc relative coords + dx = x - guest_x + dy = y - guest_y + */ + x -= gui_data->guest_x; + y -= gui_data->guest_y; + gui_data->guest_x += x; + gui_data->guest_y += y; + dx = x; + dy = y; + } + + parea->callback(parea->opaque, dx, dy, dz, state); +} + +static void gui_loaded_notify_toggle_fullscreen(void) +{ + gui_data->gui_fullscreen = !gui_data->gui_fullscreen; + + gui_data->host_callbacks.set_screen_size( + gui_data->screen_data.width, + gui_data->screen_data.height, + gui_data->gui_fullscreen); + + gui_data->host_callbacks.get_screen_data(&gui_data->screen_data); + + gui_notify_console_select_by_vtid(current_vt_id()); + +} + +static void gui_loaded_notify_mouse_motion(int dx, int dy, int dz, int x, int y, int state) +{ + if (vt_enabled()) { + if (gui_grabbing() /*|| kbd_mouse_is_absolute() || + absolute_enabled*/) { + gui_loaded_send_mouse_event(gui_data->current_vt->clickable_map.currently_grabbing, dx, dy, dz, x, y, state); + } + } +} + +static void gui_loaded_notify_mouse_button(int dz, int x, int y, int state) +{ + if (vt_enabled()) { + clickable_area_t *forward_area = NULL; + + /* If we're not grabbing, check if we should start due to click on a pointerarea */ + + if (grabbing_mouse()) + forward_area = gui_data->current_vt->clickable_map.currently_grabbing; + else { + AreaID id; + clickable_area_t *carea = find_clickarea(gui_data->current_vt, x, y, &id); + + if (carea != NULL) { + if (carea->type == CA_BUTTON) { + if (state == MOUSE_EVENT_LBUTTON) { + if (gui_data->current_vt->buttons[carea->id].pressed_img_id > 0) + gui_show_image(current_vt_id(), + gui_data->current_vt->buttons[carea->id].pressed_img_id); + + button_actions[gui_data->current_vt->buttons[carea->id].actions].down(gui_data->current_vt->buttons[carea->id].parameter); + } else { + gui_hide_image(current_vt_id(), + gui_data->current_vt->buttons[carea->id].pressed_img_id); + button_actions[gui_data->current_vt->buttons[carea->id].actions].up(gui_data->current_vt->buttons[carea->id].parameter); + } + } else { /* pointerarea*/ + /* get the associated pointerarea */ + pointer_area_t * const parea = &gui_data->current_vt->pointerareas[carea->id]; + if (parea->grab_on_click) { + if (state == MOUSE_EVENT_LBUTTON) { + gui_data->current_vt->clickable_map.currently_grabbing = carea; + gui_loaded_grab_start(); + } /* else: ignore */ + } else + forward_area = carea; + } + } + } + + if (forward_area != NULL) + gui_loaded_send_mouse_event(forward_area, 0, 0, dz, x, y, state); + } +} + +static void gui_loaded_notify_mouse_warp(int x, int y, int on) +{ + if (on) { + if (!gui_data->guest_cursor) + gui_loaded_show_cursor(); + if (gui_grabbing() /*|| kbd_mouse_is_absolute() || absolute_enabled*/) { + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); + gui_data->host_callbacks.mouse_warp(gui_data->guest_x, + gui_data->guest_y); + } + } else if (gui_grabbing()) + gui_loaded_hide_cursor(); + gui_data->guest_cursor = on; + gui_data->guest_x = x; + gui_data->guest_y = y; +} + +static void gui_loaded_notify_toggle_grabmode(void) +{ + if (vt_enabled()) { + if (!gui_grabbing()) { +#if 0 + /* TODO DFG : distinguish between mouse and kbd grabbing. Do this when + implementing VNC support. */ + int app_active = 1; + + /* if the application is not active, + do not try to enter grab state. It + prevents + 'SDL_WM_GrabInput(SDL_GRAB_ON)' + from blocking all the application + (SDL bug). */ + if (gui_data->host_callbacks.is_app_active != NULL) + app_active = gui_data->host_callbacks.is_app_active(); + + if (app_active) + gui_grab_start(); +#endif + } else { + gui_loaded_grab_end(); + } + } +} + +static void gui_loaded_notify_new_guest_cursor(void) +{ + /* DFG: DANGER, this call also invokes show_cursor() */ + if (gui_data->guest_cursor && + (gui_grabbing() /*|| kbd_mouse_is_absolute() || absolute_enabled*/)) + gui_data->host_callbacks.turn_cursor_on(GUI_CURSOR_GUEST_SPRITE); +} + +static void gui_loaded_notify_input_focus_lost(void) +{ + if (gui_grabbing()) + gui_loaded_grab_end(); +} + +static void gui_loaded_notify_app_focus(int gain) +{ + gui_unloaded_notify_app_focus(gain); +} + + +/**************************************************************************/ + +const gui_input_table_t input_tables[2] = { + { + gui_unloaded_notify_toggle_fullscreen, + gui_unloaded_notify_toggle_grabmode, + gui_unloaded_notify_mouse_motion, + gui_unloaded_notify_mouse_button, + gui_unloaded_notify_mouse_warp, + gui_unloaded_notify_new_guest_cursor, + gui_unloaded_notify_input_focus_lost, + gui_unloaded_notify_app_focus, + }, + { + gui_loaded_notify_toggle_fullscreen, + gui_loaded_notify_toggle_grabmode, + gui_loaded_notify_mouse_motion, + gui_loaded_notify_mouse_button, + gui_loaded_notify_mouse_warp, + gui_loaded_notify_new_guest_cursor, + gui_loaded_notify_input_focus_lost, + gui_loaded_notify_app_focus, + }, +}; + +static void gui_set_input_state(enum gui_input_state_t input_state) +{ + gui_data->input_table = &input_tables[input_state]; +} +