symbian-qemu-0.9.1-12/qemu-symbian-svp/gui.c
changeset 1 2fb8b9db1c86
child 61 42fe4f76a74e
--- /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; 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];
+}
+