/*
* 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; i<gui_data->vts[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];
}