summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2010-09-14 15:37:18 +0200
committerGerd Hoffmann <kraxel@redhat.com>2010-09-14 15:37:18 +0200
commit4fbbd7a2109d82f67f409e423e281fc71544f9a4 (patch)
treed477237f7f144da43d2c6f1eae0945eefd7e827f
parent1f8613b8839dfd7356823d518fa73e3cc8e01895 (diff)
agent stuff.
widget properties.
-rw-r--r--gtk/channel-main.c53
-rw-r--r--gtk/spice-channel-priv.h5
-rw-r--r--gtk/spice-widget.c162
-rw-r--r--gtk/spicy.c54
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);