diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-14 15:37:18 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-14 15:37:18 +0200 |
commit | 4fbbd7a2109d82f67f409e423e281fc71544f9a4 (patch) | |
tree | d477237f7f144da43d2c6f1eae0945eefd7e827f | |
parent | 1f8613b8839dfd7356823d518fa73e3cc8e01895 (diff) |
agent stuff.
widget properties.
-rw-r--r-- | gtk/channel-main.c | 53 | ||||
-rw-r--r-- | gtk/spice-channel-priv.h | 5 | ||||
-rw-r--r-- | gtk/spice-widget.c | 162 | ||||
-rw-r--r-- | gtk/spicy.c | 54 |
4 files changed, 256 insertions, 18 deletions
diff --git a/gtk/channel-main.c b/gtk/channel-main.c index c5c8300..0545480 100644 --- a/gtk/channel-main.c +++ b/gtk/channel-main.c @@ -2,6 +2,7 @@ #include "spice-channel-priv.h" #include "spice-session-priv.h" +#include <assert.h> #include <spice/vd_agent.h> static void agent_monitors_config(SpiceChannel *channel) @@ -28,6 +29,7 @@ static void agent_monitors_config(SpiceChannel *channel) mon = (VDAgentMonitorsConfig*) spice_marshaller_reserve_space(out->marshaller, size); + msg->protocol = VD_AGENT_PROTOCOL; msg->type = VD_AGENT_MONITORS_CONFIG; msg->opaque = 0; msg->size = size; @@ -38,11 +40,14 @@ static void agent_monitors_config(SpiceChannel *channel) mon->monitors[i].depth = 32; mon->monitors[i].width = c->main.display[i].width; mon->monitors[i].height = c->main.display[i].height; - mon->monitors[i].x = 0; - mon->monitors[i].y = 0; + mon->monitors[i].x = c->main.display[i].x; + mon->monitors[i].y = c->main.display[i].y; + fprintf(stderr, "%s: #%d %dx%d+%d+%d @ %d bpp\n", __FUNCTION__, i, + mon->monitors[i].width, mon->monitors[i].height, + mon->monitors[i].x, mon->monitors[i].y, + mon->monitors[i].depth); } - fprintf(stderr, "%s: sending\n", __FUNCTION__); spice_msg_out_send(out); spice_msg_out_put(out); } @@ -184,12 +189,48 @@ static void main_handle_agent_connected(SpiceChannel *channel, spice_msg_in *in) static void main_handle_agent_disconnected(SpiceChannel *channel, spice_msg_in *in) { - fprintf(stderr, "%s: TODO\n", __FUNCTION__); + spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); + + c->main.agent_connected = false; } static void main_handle_agent_data(SpiceChannel *channel, spice_msg_in *in) { - fprintf(stderr, "%s: TODO\n", __FUNCTION__); + spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); + VDAgentMessage *msg; + + spice_msg_in_hexdump(in); + + if (!c->main.agent_msg) { + assert(in->dpos > sizeof(VDAgentMessage)); + msg = (VDAgentMessage *)in->data; + if (msg->size + sizeof(VDAgentMessage) > in->dpos) { + fprintf(stderr, "%s: TODO: start buffer\n", __FUNCTION__); + } else { + assert(msg->size + sizeof(VDAgentMessage) == in->dpos); + goto complete; + } + } else { + fprintf(stderr, "%s: TODO: fill buffer\n", __FUNCTION__); + } + return; + +complete: + switch (msg->type) { + case VD_AGENT_REPLY: + { + VDAgentReply *reply = (VDAgentReply*)(msg+1); + fprintf(stderr, "%s: reply: type %d, %s\n", __FUNCTION__, reply->type, + reply->error == VD_AGENT_SUCCESS ? "success" : "error"); + break; + } + case VD_AGENT_CLIPBOARD: + fprintf(stderr, "%s: clipboard\n", __FUNCTION__); + break; + default: + fprintf(stderr, "unsupported agent message type %u size %u\n", + msg->type, msg->size); + } } static void main_handle_agent_token(SpiceChannel *channel, spice_msg_in *in) @@ -231,6 +272,8 @@ void spice_main_set_display(SpiceChannel *channel, int id, spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); if (id < SPICE_N_ELEMENTS(c->main.display)) { + c->main.display[id].x = x; + c->main.display[id].y = y; c->main.display[id].width = width; c->main.display[id].height = height; agent_monitors_config(channel); diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h index 7e1dbc5..14af264 100644 --- a/gtk/spice-channel-priv.h +++ b/gtk/spice-channel-priv.h @@ -42,7 +42,12 @@ struct main_channel { enum SpiceMouseMode mouse_mode; int agent_connected; int agent_tokens; + uint8_t *agent_msg; + uint8_t *agent_msg_pos; + uint8_t *agent_msg_size; struct { + int x; + int y; int width; int height; } display[1]; diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c index 9ec3866..c514dea 100644 --- a/gtk/spice-widget.c +++ b/gtk/spice-widget.c @@ -1,6 +1,8 @@ #include "spice-widget.h" #include "vncdisplaykeymap.h" +#include <assert.h> + #include <sys/ipc.h> #include <sys/shm.h> @@ -15,6 +17,11 @@ struct spice_display { gint channel_id; + /* options */ + bool keyboard_grab_enable; + bool resize_guest_enable; + + /* state */ enum SpiceSurfaceFmt format; gint width, height, stride; gint shmid; @@ -40,9 +47,10 @@ struct spice_display { bool mouse_have_pointer; GdkCursor *mouse_cursor; - bool keyboard_grab_enable; bool keyboard_grab_active; bool keyboard_have_focus; + int keyboard_grab_count; + time_t keyboard_grab_time; const guint16 const *keycode_map; size_t keycode_maplen; @@ -52,7 +60,15 @@ struct spice_display { G_DEFINE_TYPE(SpiceDisplay, spice_display, GTK_TYPE_DRAWING_AREA) +/* Properties */ +enum { + PROP_0, + PROP_KEYBOARD_GRAB, + PROP_RESIZE_GUEST, +}; + #if 0 +/* Signals */ enum { SPICE_DISPLAY_FOO, SPICE_DISPLAY_LAST_SIGNAL, @@ -63,6 +79,10 @@ static guint signals[SPICE_DISPLAY_LAST_SIGNAL]; static bool no_mitshm; +static void try_keyboard_grab(GtkWidget *widget); +static void try_keyboard_ungrab(GtkWidget *widget); +static void recalc_geometry(GtkWidget *widget); + /* ---------------------------------------------------------------- */ static struct format_table { @@ -98,6 +118,60 @@ static struct format_table { /* ---------------------------------------------------------------- */ +static void spice_display_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpiceDisplay *display = SPICE_DISPLAY(object); + spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display); + + switch (prop_id) { + case PROP_KEYBOARD_GRAB: + g_value_set_boolean(value, d->keyboard_grab_enable); + break; + case PROP_RESIZE_GUEST: + g_value_set_boolean(value, d->resize_guest_enable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void spice_display_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpiceDisplay *display = SPICE_DISPLAY(object); + spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display); + + switch (prop_id) { + case PROP_KEYBOARD_GRAB: + d->keyboard_grab_enable = g_value_get_boolean(value); + if (d->keyboard_grab_enable) { + try_keyboard_grab(GTK_WIDGET(display)); + } else { + try_keyboard_ungrab(GTK_WIDGET(display)); + } + break; + case PROP_RESIZE_GUEST: + d->resize_guest_enable = g_value_get_boolean(value); + if (d->resize_guest_enable) { + gtk_widget_set_size_request(GTK_WIDGET(display), 320, 240); + recalc_geometry(GTK_WIDGET(display)); + } else { + gtk_widget_set_size_request(GTK_WIDGET(display), + d->width, d->height); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + static void spice_display_destroy(GtkObject *obj) { GTK_OBJECT_CLASS(spice_display_parent_class)->destroy(obj); @@ -131,13 +205,16 @@ static void spice_display_init(SpiceDisplay *display) d->keycode_map = vnc_display_keymap_gdk2xtkbd_table(&d->keycode_maplen); d->have_mitshm = true; - d->keyboard_grab_enable = true; } static void try_keyboard_grab(GtkWidget *widget) { SpiceDisplay *display = SPICE_DISPLAY(widget); spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display); + time_t now; + + if (d->keyboard_grab_active) + return; if (!d->keyboard_grab_enable) return; @@ -146,7 +223,33 @@ static void try_keyboard_grab(GtkWidget *widget) if (!d->mouse_have_pointer) return; -// fprintf(stderr, "grab keyboard\n"); +#if 1 + /* + * == DEBUG == + * focus / keyboard grab behavior is funky + * when going fullscreen (with KDE): + * focus-in-event -> grab -> focus-out-event -> ungrab -> repeat + * I have no idea why the grab triggers focus-out :-( + */ + assert(gtk_widget_is_focus(widget)); + assert(gtk_widget_has_focus(widget)); + + now = time(NULL); + if (d->keyboard_grab_time != now) { + d->keyboard_grab_time = now; + d->keyboard_grab_count = 0; + } + if (d->keyboard_grab_count++ > 32) { + fprintf(stderr, "%s: 32 grabs last second -> emergency exit\n", + __FUNCTION__); + return; + } +#endif + +#if 0 + fprintf(stderr, "grab keyboard\n"); +#endif + gdk_keyboard_grab(gtk_widget_get_window(widget), FALSE, GDK_CURRENT_TIME); d->keyboard_grab_active = true; @@ -161,8 +264,11 @@ static void try_keyboard_ungrab(GtkWidget *widget) if (!d->keyboard_grab_active) return; -// fprintf(stderr, "ungrab keyboard\n"); +#if 0 + fprintf(stderr, "ungrab keyboard\n"); +#endif gdk_keyboard_ungrab(GDK_CURRENT_TIME); + d->keyboard_grab_active = false; } static gboolean geometry_timer(gpointer data) @@ -172,7 +278,7 @@ static gboolean geometry_timer(gpointer data) d->timer_id = 0; spice_main_set_display(d->main, d->channel_id, - 0, 0, d->width, d->height); + 0, 0, d->ww, d->wh); return false; } @@ -187,13 +293,18 @@ static void recalc_geometry(GtkWidget *widget) d->mx = (d->ww - d->width) / 2; if (d->wh > d->height) d->my = (d->wh - d->height) / 2; + +#if 0 fprintf(stderr, "%s: guest %dx%d, window %dx%d, offset +%d+%d\n", __FUNCTION__, d->width, d->height, d->ww, d->wh, d->mx, d->my); +#endif if (d->timer_id) { g_source_remove(d->timer_id); } - d->timer_id = g_timeout_add_seconds(1, geometry_timer, display); + if (d->resize_guest_enable) { + d->timer_id = g_timeout_add_seconds(1, geometry_timer, display); + } } static XVisualInfo *get_visual_for_format(GtkWidget *widget, enum SpiceSurfaceFmt format) @@ -345,7 +456,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose) expose->area.y >= d->my && expose->area.x + expose->area.width <= d->mx + d->width && expose->area.y + expose->area.height <= d->my + d->height) { - /* update is completely inside the guest screen -- blit it */ + /* area is completely inside the guest screen -- blit it */ if (d->have_mitshm) { XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window), d->gc, d->ximage, @@ -542,7 +653,7 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button) button->type == GDK_BUTTON_PRESS ? "press" : "release", button->button); #endif - gtk_widget_grab_focus (widget); + gtk_widget_grab_focus(widget); if (!d->inputs) return true; @@ -600,8 +711,35 @@ static void spice_display_class_init(SpiceDisplayClass *klass) gtkobject_class->destroy = spice_display_destroy; gobject_class->finalize = spice_display_finalize; - - g_type_class_add_private(klass, sizeof(spice_display)); + gobject_class->get_property = spice_display_get_property; + gobject_class->set_property = spice_display_set_property; + + g_object_class_install_property + (gobject_class, PROP_KEYBOARD_GRAB, + g_param_spec_boolean("grab-keyboard", + "Grab Keyboard", + "Whether we should grab the keyboard.", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (gobject_class, PROP_RESIZE_GUEST, + g_param_spec_boolean("resize-guest", + "Resize guest", + "Try to adapt guest display on window resize. " + "Requires guest cooperation.", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_type_class_add_private(klass, sizeof(spice_display)); } /* ---------------------------------------------------------------- */ @@ -632,7 +770,9 @@ static void primary_create(SpiceChannel *channel, gint format, d->width = width; d->height = height; recalc_geometry(GTK_WIDGET(display)); - gtk_widget_set_size_request(GTK_WIDGET(display), width, height); + if (!d->resize_guest_enable) { + gtk_widget_set_size_request(GTK_WIDGET(display), width, height); + } } } diff --git a/gtk/spicy.c b/gtk/spicy.c index 6a7c41c..4ec9b52 100644 --- a/gtk/spicy.c +++ b/gtk/spicy.c @@ -15,6 +15,7 @@ struct spice_window { /* config */ static char *host = "localhost"; static char *port = "5920"; +static bool fullscreen = false; /* state */ static SpiceSession *session; @@ -40,6 +41,18 @@ static void menu_cb_fullscreen(GtkAction *action, void *data) } } +static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data) +{ + struct spice_window *win = data; + gboolean state = gtk_toggle_action_get_active(action); + + fprintf(stderr, "%s: %s = %s\n", __FUNCTION__, + gtk_action_get_name(GTK_ACTION(action)), state ? "yes" : "no"); + g_object_set(G_OBJECT(win->spice), + gtk_action_get_name(GTK_ACTION(action)), state, + NULL); +} + static void menu_cb_about(GtkAction *action, void *data) { static char *comments = "gtk client app for the\n" @@ -80,6 +93,7 @@ static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, gtk_widget_hide(win->menubar); gtk_widget_hide(win->toolbar); gtk_widget_hide(win->fstatus); + gtk_widget_grab_focus(win->spice); } else { gtk_widget_show(win->menubar); gtk_widget_show(win->toolbar); @@ -99,6 +113,9 @@ static const GtkActionEntry entries[] = { .name = "ViewMenu", .label = "_View", },{ + .name = "OptionMenu", + .label = "_Options", + },{ .name = "HelpMenu", .label = "_Help", },{ @@ -127,6 +144,18 @@ static const GtkActionEntry entries[] = { } }; +static const GtkToggleActionEntry tentries[] = { + { + .name = "grab-keyboard", + .label = "Grab keyboard", + .callback = G_CALLBACK(menu_cb_bool_prop), + },{ + .name = "resize-guest", + .label = "Resize guest", + .callback = G_CALLBACK(menu_cb_bool_prop), + } +}; + static char ui_xml[] = "<ui>\n" " <menubar action='MainMenu'>\n" @@ -136,6 +165,10 @@ static char ui_xml[] = " <menu action='ViewMenu'>\n" " <menuitem action='Fullscreen'/>\n" " </menu>\n" +" <menu action='OptionMenu'>\n" +" <menuitem action='grab-keyboard'/>\n" +" <menuitem action='resize-guest'/>\n" +" </menu>\n" " <menu action='HelpMenu'>\n" " <menuitem action='About'/>\n" " </menu>\n" @@ -153,6 +186,7 @@ static spice_window *create_spice_window(SpiceSession *s, int id) struct spice_window *win; GtkWidget *vbox; GError *err = NULL; + int i; win = malloc(sizeof(*win)); if (NULL == win) @@ -164,7 +198,6 @@ static spice_window *create_spice_window(SpiceSession *s, int id) win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); snprintf(title, sizeof(title), "spice display %d", id); gtk_window_set_title(GTK_WINDOW(win->toplevel), title); - gtk_window_set_default_size(GTK_WINDOW(win->toplevel), 640, 480); g_signal_connect(G_OBJECT(win->toplevel), "destroy", G_CALLBACK(destroy_cb), win); g_signal_connect(G_OBJECT(win->toplevel), "window-state-event", @@ -174,6 +207,8 @@ static spice_window *create_spice_window(SpiceSession *s, int id) win->ui = gtk_ui_manager_new(); win->ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(win->ag, entries, G_N_ELEMENTS(entries), win); + gtk_action_group_add_toggle_actions(win->ag, tentries, + G_N_ELEMENTS(tentries), win); gtk_ui_manager_insert_action_group(win->ui, win->ag, 0); win->accel = gtk_ui_manager_get_accel_group(win->ui); gtk_window_add_accel_group(GTK_WINDOW(win->toplevel), win->accel); @@ -206,7 +241,18 @@ static spice_window *create_spice_window(SpiceSession *s, int id) gtk_box_pack_end(GTK_BOX(vbox), win->fstatus, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(win->fstatus), win->status); + /* init toggle actions */ + for (i = 0; i < G_N_ELEMENTS(tentries); i++) { + GtkAction *toggle; + gboolean state; + toggle = gtk_action_group_get_action(win->ag, tentries[i].name); + g_object_get(win->spice, tentries[i].name, &state, NULL); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state); + } + /* show window */ + if (fullscreen) + gtk_window_fullscreen(GTK_WINDOW(win->toplevel)); gtk_widget_show_all(win->toplevel); return win; } @@ -262,6 +308,7 @@ static void usage(FILE *fp) "options:\n" " -h host [ %s ]\n" " -p port [ %s ]\n" + " -f fullscreen\n" "\n", host, port); } @@ -273,7 +320,7 @@ int main(int argc, char *argv[]) /* parse opts */ gtk_init(&argc, &argv); for (;;) { - if (-1 == (c = getopt(argc, argv, "h:p:"))) + if (-1 == (c = getopt(argc, argv, "h:p:f"))) break; switch (c) { case 'h': @@ -282,6 +329,9 @@ int main(int argc, char *argv[]) case 'p': port = optarg; break; + case 'f': + fullscreen = true; + break; #if 0 /* --help */ case 'h': usage(stdout); |