diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-15 16:13:20 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-15 16:13:20 +0200 |
commit | 74553154d842202533dded90b32d7a1cad94da35 (patch) | |
tree | 51cdfb17e7addb2a563b0e4556079b49c2cc1e43 | |
parent | 830519ef528c6b7fe296be5c03156dfdb60a1f79 (diff) |
tls bits.
server mouse mode.
-rw-r--r-- | gtk/README | 6 | ||||
-rw-r--r-- | gtk/channel-base.c | 2 | ||||
-rw-r--r-- | gtk/channel-display.c | 27 | ||||
-rw-r--r-- | gtk/channel-main.c | 53 | ||||
-rw-r--r-- | gtk/spice-channel-priv.h | 1 | ||||
-rw-r--r-- | gtk/spice-channel.c | 52 | ||||
-rw-r--r-- | gtk/spice-channel.h | 8 | ||||
-rw-r--r-- | gtk/spice-session.c | 2 | ||||
-rw-r--r-- | gtk/spice-widget.c | 122 | ||||
-rw-r--r-- | gtk/spice-widget.h | 3 | ||||
-rw-r--r-- | gtk/spicy.c | 25 |
11 files changed, 238 insertions, 63 deletions
@@ -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" |