Fix for Bug 3671 - QEMU GDB stub listens on IPv6-only port on Windows 7
The connection string used by the GDB stub does not specify which
version of the Internet Protocol should be used by the port on
which it listens. On host platforms with IPv6 support, such as
Windows 7, this means that the stub listens on an IPv6-only port.
Since the GDB client uses IPv4, this means that the client cannot
connect to QEMU.
/*
* 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.
*
* Contributors:
* NTT DOCOMO, INC. -- Clicking a QEMU skin button causes screen flicker on the display area
*
*/
#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 {
if (gui_data->current_vt->buttons[carea->id].pressed_img_id > 0)
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];
}