summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2010-09-15 16:13:20 +0200
committerGerd Hoffmann <kraxel@redhat.com>2010-09-15 16:13:20 +0200
commit74553154d842202533dded90b32d7a1cad94da35 (patch)
tree51cdfb17e7addb2a563b0e4556079b49c2cc1e43
parent830519ef528c6b7fe296be5c03156dfdb60a1f79 (diff)
tls bits.
server mouse mode.
-rw-r--r--gtk/README6
-rw-r--r--gtk/channel-base.c2
-rw-r--r--gtk/channel-display.c27
-rw-r--r--gtk/channel-main.c53
-rw-r--r--gtk/spice-channel-priv.h1
-rw-r--r--gtk/spice-channel.c52
-rw-r--r--gtk/spice-channel.h8
-rw-r--r--gtk/spice-session.c2
-rw-r--r--gtk/spice-widget.c122
-rw-r--r--gtk/spice-widget.h3
-rw-r--r--gtk/spicy.c25
11 files changed, 238 insertions, 63 deletions
diff --git a/gtk/README b/gtk/README
index 3a76e99..efecc73 100644
--- a/gtk/README
+++ b/gtk/README
@@ -10,11 +10,9 @@ current state
Starts becoming usable.
-Lots of features are missing:
- - No TLS support.
- - No server mouse mode support.
+Some features are missing:
- No sound support.
- - No vdagent support.
+ - No client migration support.
- No library documentation.
- Probably more ...
diff --git a/gtk/channel-base.c b/gtk/channel-base.c
index d13547b..d1aef33 100644
--- a/gtk/channel-base.c
+++ b/gtk/channel-base.c
@@ -47,7 +47,7 @@ void base_handle_notify(SpiceChannel *channel, spice_msg_in *in)
if (notify->message_len &&
notify->message_len <= in->dpos - sizeof(*notify)) {
- message_str = notify->message;
+ message_str = (char*)notify->message;
}
fprintf(stderr, "%s: channel %u:%u -- %s%s #%u%s%.*s\n", __FUNCTION__,
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 4493346..3ed5b8b 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -199,7 +199,32 @@ static void display_ready(SpiceChannel *channel)
static void display_handle_mode(SpiceChannel *channel, spice_msg_in *in)
{
- fprintf(stderr, "%s: TODO\n", __FUNCTION__);
+ spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
+ SpiceMsgDisplayMode *mode = spice_msg_in_parsed(in);
+ display_surface *surface = find_surface(channel, 0);
+
+ if (surface) {
+ g_signal_emit(channel, channel_signals[SPICE_DISPLAY_PRIMARY_DESTROY], 0);
+ ring_remove(&surface->link);
+ destroy_canvas(surface);
+ free(surface);
+ }
+
+ fprintf(stderr, "%s: %dx%d @ %d bpp\n", __FUNCTION__,
+ mode->x_res, mode->y_res, mode->bits);
+ surface = spice_new0(display_surface, 1);
+ surface->format = mode->bits == 32 ?
+ SPICE_SURFACE_FMT_32_xRGB : SPICE_SURFACE_FMT_16_555;
+ surface->width = mode->x_res;
+ surface->height = mode->y_res;
+ surface->stride = surface->width * 4;
+ surface->size = surface->height * surface->stride;
+ surface->primary = true;
+ create_canvas(channel, surface);
+ g_signal_emit(channel, channel_signals[SPICE_DISPLAY_PRIMARY_CREATE], 0,
+ surface->format, surface->width, surface->height,
+ surface->stride, surface->shmid, surface->data);
+ ring_add(&c->display.surfaces, &surface->link);
}
static void display_handle_mark(SpiceChannel *channel, spice_msg_in *in)
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 0545480..3b80da1 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -60,7 +60,10 @@ static void agent_start(SpiceChannel *channel)
};
spice_msg_out *out;
- fprintf(stderr, "%s\n", __FUNCTION__);
+ c->main.agent_connected = true;
+ g_signal_emit(channel, channel_signals[SPICE_MAIN_AGENT_EVENT], 0,
+ SPICE_AGENT_CONNECT);
+
out = spice_msg_out_new(channel, SPICE_MSGC_MAIN_AGENT_START);
c->marshallers->msgc_main_agent_start(out->marshaller, &agent_start);
spice_msg_out_send(out);
@@ -69,14 +72,21 @@ static void agent_start(SpiceChannel *channel)
agent_monitors_config(channel);
}
+static void agent_stopped(SpiceChannel *channel)
+{
+ spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
+
+ c->main.agent_connected = false;
+ g_signal_emit(channel, channel_signals[SPICE_MAIN_AGENT_EVENT], 0,
+ SPICE_AGENT_DISCONNECT);
+}
+
static void set_mouse_mode(SpiceChannel *channel, uint32_t supported, uint32_t current)
{
spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
if (c->main.mouse_mode != current) {
c->main.mouse_mode = current;
- fprintf(stderr, "%s: %s\n", __FUNCTION__,
- current == SPICE_MOUSE_MODE_SERVER ? "server" : "client");
g_signal_emit(channel, channel_signals[SPICE_MAIN_MOUSE_MODE], 0, current);
}
@@ -108,40 +118,12 @@ static void main_handle_init(SpiceChannel *channel, spice_msg_in *in)
set_mouse_mode(channel, init->supported_mouse_modes, init->current_mouse_mode);
c->main.agent_tokens = init->agent_tokens;
- c->main.agent_connected = !!init->agent_connected;
- if (c->main.agent_connected) {
+ if (init->agent_connected) {
agent_start(channel);
}
#if 0
set_mm_time(init->multi_media_time);
- calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
- _glz_window.set_pixels_capacity(_glz_window_size);
- set_mouse_mode(init->supported_mouse_modes, init->current_mouse_mode);
- _agent_tokens = init->agent_tokens;
- _agent_connected = !!init->agent_connected;
- if (_agent_connected) {
- Message* msg = new Message(SPICE_MSGC_MAIN_AGENT_START);
- SpiceMsgcMainAgentStart agent_start;
- agent_start.num_tokens = ~0;
- _marshallers->msgc_main_agent_start(msg->marshaller(), &agent_start);
- post_message(msg);
- }
-
- if (_agent_connected) {
- if (_auto_display_res) {
- send_agent_monitors_config();
- }
- // not sending the color depth through send_agent_monitors_config, since
- // it applies only for attached screens.
- send_agent_display_config();
- }
-
- if (!_auto_display_res && _display_setting.is_empty()) {
- post_message(new Message(SPICE_MSGC_MAIN_ATTACH_CHANNELS));
- } else {
- _application.activate_interval_timer(*_agent_timer, AGENT_TIMEOUT);
- }
#endif
}
@@ -181,17 +163,12 @@ static void main_handle_mouse_mode(SpiceChannel *channel, spice_msg_in *in)
static void main_handle_agent_connected(SpiceChannel *channel, spice_msg_in *in)
{
- spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
-
- c->main.agent_connected = true;
agent_start(channel);
}
static void main_handle_agent_disconnected(SpiceChannel *channel, spice_msg_in *in)
{
- spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
-
- c->main.agent_connected = false;
+ agent_stopped(channel);
}
static void main_handle_agent_data(SpiceChannel *channel, spice_msg_in *in)
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index b411ca3..3cd92a5 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -152,6 +152,7 @@ enum {
SPICE_CHANNEL_EVENT,
SPICE_MAIN_MOUSE_MODE,
+ SPICE_MAIN_AGENT_EVENT,
SPICE_DISPLAY_PRIMARY_CREATE,
SPICE_DISPLAY_PRIMARY_DESTROY,
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 6c980a2..b997b9d 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -69,6 +69,17 @@ static void spice_channel_class_init(SpiceChannelClass *klass)
1,
G_TYPE_INT);
+ channel_signals[SPICE_MAIN_AGENT_EVENT] =
+ g_signal_new("spice-main-agent-event",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SpiceChannelClass, spice_main_agent_event),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
channel_signals[SPICE_DISPLAY_PRIMARY_CREATE] =
g_signal_new("spice-display-primary-create",
G_OBJECT_CLASS_TYPE(gobject_class),
@@ -316,7 +327,8 @@ static void spice_channel_tls_connect(SpiceChannel *channel)
if (err == SSL_ERROR_WANT_READ) {
return;
}
- PANIC("SSL_connect failed: %s", ERR_error_string(err, NULL));
+ fprintf(stderr, "SSL_connect: %s", ERR_error_string(err, NULL));
+ spice_channel_emit_event(channel, SPICE_CHANNEL_ERROR_TLS);
}
c->state = SPICE_CHANNEL_STATE_LINK_HDR;
spice_channel_send_link(channel);
@@ -329,13 +341,10 @@ static void spice_channel_send_auth(SpiceChannel *channel)
int nRSASize;
BIO *bioKey;
RSA *rsa;
- const uint8_t *password;
+ const char *password;
uint8_t *encrypted;
int rc;
- g_object_get(c->session, "password", &password, NULL);
- fprintf(stderr, "%s: password \"%s\"\n", __FUNCTION__, password);
-
bioKey = BIO_new(BIO_s_mem());
if (bioKey == NULL)
PANIC("Could not initiate BIO");
@@ -350,9 +359,11 @@ static void spice_channel_send_auth(SpiceChannel *channel)
The use of RSA encryption limit the potential maximum password length.
for RSA_PKCS1_OAEP_PADDING it is RSA_size(rsa) - 41.
*/
- rc = RSA_public_encrypt(strlen((char*)password) + 1,
- password, encrypted,
- rsa, RSA_PKCS1_OAEP_PADDING);
+ g_object_get(c->session, "password", &password, NULL);
+ if (password == NULL)
+ password = "";
+ rc = RSA_public_encrypt(strlen(password) + 1, (uint8_t*)password,
+ encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
if (rc <= 0)
PANIC("could not encrypt password");
spice_channel_send(channel, encrypted, nRSASize);
@@ -449,9 +460,18 @@ static void spice_channel_recv_link_hdr(SpiceChannel *channel)
PANIC("incomplete link header (%d/%zd)", rc, sizeof(c->peer_hdr));
if (c->peer_hdr.magic != SPICE_MAGIC)
PANIC("bad magic");
- if (c->peer_hdr.major_version != c->link_hdr.major_version)
+ if (c->peer_hdr.major_version != c->link_hdr.major_version) {
+ if (c->peer_hdr.major_version == 1) {
+ /* enter spice 0.4 mode */
+ c->protocol = 1;
+ fprintf(stderr, "switching to protocol 1 (spice 0.4)\n");
+ spice_channel_disconnect(channel, SPICE_CHANNEL_NONE);
+ spice_channel_connect(channel);
+ return;
+ }
PANIC("major mismatch (got %d, expected %d)",
c->peer_hdr.major_version, c->link_hdr.major_version);
+ }
c->peer_msg = spice_malloc(c->peer_hdr.size);
c->state = SPICE_CHANNEL_STATE_LINK_MSG;
@@ -471,6 +491,7 @@ static void spice_channel_recv_link_msg(SpiceChannel *channel)
break;
case SPICE_LINK_ERR_NEED_SECURED:
c->tls = true;
+ fprintf(stderr, "switching to tls\n");
spice_channel_disconnect(channel, SPICE_CHANNEL_NONE);
spice_channel_connect(channel);
return;
@@ -737,7 +758,8 @@ reconnect:
c->state = SPICE_CHANNEL_STATE_TLS;
return 0;
}
- PANIC("SSL_connect failed: %s", ERR_error_string(err, NULL));
+ fprintf(stderr, "SSL_connect: %s", ERR_error_string(err, NULL));
+ spice_channel_emit_event(channel, SPICE_CHANNEL_ERROR_TLS);
}
}
@@ -754,6 +776,16 @@ void spice_channel_disconnect(SpiceChannel *channel, enum SpiceChannelEvent reas
return;
}
+ if (c->tls) {
+ if (c->ssl) {
+ SSL_free(c->ssl);
+ c->ssl = NULL;
+ }
+ if (c->ctx) {
+ SSL_CTX_free(c->ctx);
+ c->ctx = NULL;
+ }
+ }
if (c->watch) {
spice_watch_put(c->watch);
c->watch = NULL;
diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h
index 706061b..ace9c66 100644
--- a/gtk/spice-channel.h
+++ b/gtk/spice-channel.h
@@ -7,11 +7,18 @@ G_BEGIN_DECLS
#define SPICE_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_CHANNEL))
#define SPICE_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_CHANNEL, SpiceChannelClass))
+enum SpiceAgentEvent {
+ SPICE_AGENT_NONE = 0,
+ SPICE_AGENT_CONNECT,
+ SPICE_AGENT_DISCONNECT,
+};
+
enum SpiceChannelEvent {
SPICE_CHANNEL_NONE = 0,
SPICE_CHANNEL_OPENED = 10,
SPICE_CHANNEL_CLOSED,
SPICE_CHANNEL_ERROR_CONNECT = 20,
+ SPICE_CHANNEL_ERROR_TLS,
SPICE_CHANNEL_ERROR_LINK,
SPICE_CHANNEL_ERROR_AUTH,
SPICE_CHANNEL_ERROR_IO,
@@ -33,6 +40,7 @@ struct _SpiceChannelClass
/* main signals */
void (*spice_main_mouse_mode)(SpiceChannel *channel, enum SpiceMouseMode mode);
+ void (*spice_main_agent_event)(SpiceChannel *channel, enum SpiceAgentEvent event);
/* display signals */
void (*spice_display_primary_create)(SpiceChannel *channel, gint format,
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 41dc292..06462d6 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -223,8 +223,6 @@ static void spice_session_class_init(SpiceSessionClass *klass)
SPICE_TYPE_CHANNEL);
g_type_class_add_private(klass, sizeof(spice_session));
-
- tcp_verbose = 1;
}
/* ------------------------------------------------------------------ */
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index c514dea..3f4db9a 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -19,6 +19,7 @@ struct spice_display {
/* options */
bool keyboard_grab_enable;
+ bool mouse_grab_enable;
bool resize_guest_enable;
/* state */
@@ -44,8 +45,11 @@ struct spice_display {
SpiceChannel *inputs;
enum SpiceMouseMode mouse_mode;
+ int mouse_grab_active;
bool mouse_have_pointer;
GdkCursor *mouse_cursor;
+ int mouse_last_x;
+ int mouse_last_y;
bool keyboard_grab_active;
bool keyboard_have_focus;
@@ -64,6 +68,7 @@ G_DEFINE_TYPE(SpiceDisplay, spice_display, GTK_TYPE_DRAWING_AREA)
enum {
PROP_0,
PROP_KEYBOARD_GRAB,
+ PROP_MOUSE_GRAB,
PROP_RESIZE_GUEST,
};
@@ -81,6 +86,8 @@ static bool no_mitshm;
static void try_keyboard_grab(GtkWidget *widget);
static void try_keyboard_ungrab(GtkWidget *widget);
+static void try_mouse_grab(GtkWidget *widget);
+static void try_mouse_ungrab(GtkWidget *widget);
static void recalc_geometry(GtkWidget *widget);
/* ---------------------------------------------------------------- */
@@ -130,6 +137,9 @@ static void spice_display_get_property(GObject *object,
case PROP_KEYBOARD_GRAB:
g_value_set_boolean(value, d->keyboard_grab_enable);
break;
+ case PROP_MOUSE_GRAB:
+ g_value_set_boolean(value, d->mouse_grab_enable);
+ break;
case PROP_RESIZE_GUEST:
g_value_set_boolean(value, d->resize_guest_enable);
break;
@@ -156,6 +166,12 @@ static void spice_display_set_property(GObject *object,
try_keyboard_ungrab(GTK_WIDGET(display));
}
break;
+ case PROP_MOUSE_GRAB:
+ d->mouse_grab_enable = g_value_get_boolean(value);
+ if (!d->mouse_grab_enable) {
+ try_mouse_ungrab(GTK_WIDGET(display));
+ }
+ break;
case PROP_RESIZE_GUEST:
d->resize_guest_enable = g_value_get_boolean(value);
if (d->resize_guest_enable) {
@@ -271,6 +287,74 @@ static void try_keyboard_ungrab(GtkWidget *widget)
d->keyboard_grab_active = false;
}
+static void try_mouse_grab(GtkWidget *widget)
+{
+ SpiceDisplay *display = SPICE_DISPLAY(widget);
+ spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
+
+ if (!d->mouse_grab_enable)
+ return;
+ if (d->mouse_mode != SPICE_MOUSE_MODE_SERVER)
+ return;
+ if (d->mouse_grab_active)
+ return;
+
+ gdk_pointer_grab(gtk_widget_get_window(widget),
+ FALSE, /* All events to come to our window directly */
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK,
+ NULL, /* Allow cursor to move over entire desktop */
+ gdk_cursor_new(GDK_BLANK_CURSOR),
+ GDK_CURRENT_TIME);
+ d->mouse_grab_active = true;
+ d->mouse_last_x = -1;
+ d->mouse_last_y = -1;
+}
+
+static void check_mouse_edges(GtkWidget *widget, GdkEventMotion *motion)
+{
+ SpiceDisplay *display = SPICE_DISPLAY(widget);
+ spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ GdkDrawable *drawable = GDK_DRAWABLE(gtk_widget_get_window(widget));
+ GdkScreen *screen = gdk_drawable_get_screen(drawable);
+ int x = (int)motion->x_root;
+ int y = (int)motion->y_root;
+
+ /* In relative mode check to see if client pointer hit
+ * one of the screen edges, and if so move it back by
+ * 200 pixels. This is important because the pointer
+ * in the server doesn't correspond 1-for-1, and so
+ * may still be only half way across the screen. Without
+ * this warp, the server pointer would thus appear to hit
+ * an invisible wall */
+ if (x == 0) x += 200;
+ if (y == 0) y += 200;
+ if (x == (gdk_screen_get_width(screen) - 1)) x -= 200;
+ if (y == (gdk_screen_get_height(screen) - 1)) y -= 200;
+
+ if (x != (int)motion->x_root || y != (int)motion->y_root) {
+ gdk_display_warp_pointer(gdk_drawable_get_display(drawable),
+ screen, x, y);
+ d->mouse_last_x = -1;
+ d->mouse_last_y = -1;
+ }
+}
+
+static void try_mouse_ungrab(GtkWidget *widget)
+{
+ SpiceDisplay *display = SPICE_DISPLAY(widget);
+ spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
+
+ if (!d->mouse_grab_active)
+ return;
+
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
+ d->mouse_grab_active = false;
+}
+
static gboolean geometry_timer(gpointer data)
{
SpiceDisplay *display = data;
@@ -346,7 +430,6 @@ static XVisualInfo *get_visual_default(GtkWidget *widget)
static int catch_no_mitshm(Display * dpy, XErrorEvent * event)
{
- fprintf(stderr,"WARNING: MIT shared memory extention not available\n");
no_mitshm = true;
return 0;
}
@@ -398,12 +481,10 @@ static int ximage_create(GtkWidget *widget)
if (no_mitshm)
goto shm_fail;
XSetErrorHandler(old_handler);
- fprintf(stderr, "%s: mitshm OK\n", __FUNCTION__);
return 0;
}
shm_fail:
- fprintf(stderr, "%s: mitshm FAIL\n", __FUNCTION__);
d->have_mitshm = false;
if (old_handler)
XSetErrorHandler(old_handler);
@@ -508,9 +589,11 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
int scancode;
+#if 0
fprintf(stderr, "%s %s: keycode: %d state: %d group %d\n",
__FUNCTION__, key->type == GDK_KEY_PRESS ? "press" : "release",
key->hardware_keycode, key->state, key->group);
+#endif
if (!d->inputs)
return true;
@@ -635,7 +718,18 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
}
break;
case SPICE_MOUSE_MODE_SERVER:
- /* TODO */
+ if (d->mouse_grab_active) {
+ if (d->mouse_last_x != -1 &&
+ d->mouse_last_y != -1) {
+ spice_inputs_motion(d->inputs,
+ motion->x - d->mouse_last_x,
+ motion->y - d->mouse_last_y,
+ button_mask_gdk_to_spice(motion->state));
+ }
+ d->mouse_last_x = motion->x;
+ d->mouse_last_y = motion->y;
+ check_mouse_edges(widget, motion);
+ }
break;
default:
break;
@@ -654,9 +748,11 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
button->button);
#endif
gtk_widget_grab_focus(widget);
+ try_mouse_grab(widget);
if (!d->inputs)
return true;
+
switch (button->type) {
case GDK_BUTTON_PRESS:
spice_inputs_button_press(d->inputs,
@@ -727,6 +823,18 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
G_PARAM_STATIC_BLURB));
g_object_class_install_property
+ (gobject_class, PROP_MOUSE_GRAB,
+ g_param_spec_boolean("grab-mouse",
+ "Grab Mouse",
+ "Whether we should grab the mouse.",
+ 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",
@@ -750,7 +858,6 @@ static void mouse_mode(SpiceChannel *channel, enum SpiceMouseMode mode,
SpiceDisplay *display = data;
spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
- fprintf(stderr, "%s: mouse mode: %d\n", __FUNCTION__, mode);
d->mouse_mode = mode;
}
@@ -781,7 +888,6 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
SpiceDisplay *display = SPICE_DISPLAY(data);
spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
- fprintf(stderr, "%s:\n", __FUNCTION__);
d->format = 0;
d->width = 0;
d->height = 0;
@@ -894,3 +1000,7 @@ GtkWidget *spice_display_new(SpiceSession *session, int id)
return GTK_WIDGET(display);
}
+void spice_display_mouse_ungrab(GtkWidget *widget)
+{
+ try_mouse_ungrab(widget);
+}
diff --git a/gtk/spice-widget.h b/gtk/spice-widget.h
index 5ea3081..c603531 100644
--- a/gtk/spice-widget.h
+++ b/gtk/spice-widget.h
@@ -31,4 +31,5 @@ struct _SpiceDisplayClass {
GType spice_display_get_type(void);
G_END_DECLS
-GtkWidget* spice_display_new(SpiceSession *session, int id);
+GtkWidget* spice_display_new(SpiceSession *session, int id);
+void spice_display_mouse_ungrab(GtkWidget *widget);
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 40e7325..9860ea6 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -44,6 +44,13 @@ static void menu_cb_fullscreen(GtkAction *action, void *data)
}
}
+static void menu_cb_ungrab(GtkAction *action, void *data)
+{
+ struct spice_window *win = data;
+
+ spice_display_mouse_ungrab(win->spice);
+}
+
static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data)
{
struct spice_window *win = data;
@@ -116,6 +123,9 @@ static const GtkActionEntry entries[] = {
.name = "ViewMenu",
.label = "_View",
},{
+ .name = "InputMenu",
+ .label = "_Input",
+ },{
.name = "OptionMenu",
.label = "_Options",
},{
@@ -139,6 +149,13 @@ static const GtkActionEntry entries[] = {
.accelerator = "<shift>F11",
},{
+ /* Input menu */
+ .name = "UngrabMouse",
+ .label = "_Ungrab mouse",
+ .callback = G_CALLBACK(menu_cb_ungrab),
+ .accelerator = "<shift>F12",
+ },{
+
/* Help menu */
.name = "About",
.stock_id = GTK_STOCK_ABOUT,
@@ -153,6 +170,10 @@ static const GtkToggleActionEntry tentries[] = {
.label = "Grab keyboard",
.callback = G_CALLBACK(menu_cb_bool_prop),
},{
+ .name = "grab-mouse",
+ .label = "Grab mouse",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
.name = "resize-guest",
.label = "Resize guest",
.callback = G_CALLBACK(menu_cb_bool_prop),
@@ -168,8 +189,12 @@ static char ui_xml[] =
" <menu action='ViewMenu'>\n"
" <menuitem action='Fullscreen'/>\n"
" </menu>\n"
+" <menu action='InputMenu'>\n"
+" <menuitem action='UngrabMouse'/>\n"
+" </menu>\n"
" <menu action='OptionMenu'>\n"
" <menuitem action='grab-keyboard'/>\n"
+" <menuitem action='grab-mouse'/>\n"
" <menuitem action='resize-guest'/>\n"
" </menu>\n"
" <menu action='HelpMenu'>\n"