Windows grab do not exist like on X11, so we need to clip the cursor to our client window, while making sure it doesn't overlap with windows statusbar. When wrapping the cursor, we need to make sure we also stay within the clip region, or else the clip is inverted (pointer can't enter the clip region anymore), and we also lose the keyboard hook/grab. The end result works better than spicec, which didn't exclude the Windows statusbar, and was subject to losing pointer when wrapping mouse over it. Another approach I have been playing with is to clip the cursor, and process raw input messages, this will have the advantage to work even when the client is completely out of the working area (under the statusbar for example), but the complexity involved is too high for very poor benefit (interacting with a non-visible client), we could even argue that the behaviour implemented by this patch is more correct (it refuses to grab the cursor if the client isn't visible in the working area). v2: - choose the nearest monitor for clipping - the ClipRegion is in Windows coordinate, we can't use gdk warp - fix https://bugzilla.redhat.com/show_bug.cgi?id=872640 This solves the following bugs: https://bugzilla.redhat.com/show_bug.cgi?id=857430 https://bugzilla.redhat.com/show_bug.cgi?id=857389 --- gtk/spice-widget.c | 90 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c index 1858499..fd4d9e6 100644 --- a/gtk/spice-widget.c +++ b/gtk/spice-widget.c @@ -725,17 +725,66 @@ static void set_mouse_accel(SpiceDisplay *display, gboolean enabled) #endif } +#ifdef WIN32 +static gboolean win32_clip_cursor(void) +{ + RECT window, workarea, rect; + HMONITOR monitor; + MONITORINFO mi = { 0, }; + + g_return_val_if_fail(win32_window != NULL, FALSE); + + if (!GetWindowRect(win32_window, &window)) + goto error; + + monitor = MonitorFromRect(&window, MONITOR_DEFAULTTONEAREST); + g_return_val_if_fail(monitor != NULL, false); + + mi.cbSize = sizeof(mi); + if (!GetMonitorInfo(monitor, &mi)) + goto error; + workarea = mi.rcWork; + + if (!IntersectRect(&rect, &window, &workarea)) { + g_critical("error clipping cursor"); + return false; + } + + SPICE_DEBUG("clip rect %ld %ld %ld %ld\n", + rect.left, rect.right, rect.top, rect.bottom); + + if (!ClipCursor(&rect)) + goto error; + + return true; + +error: + { + DWORD errval = GetLastError(); + gchar *errstr = g_win32_error_message(errval); + g_warning("failed to clip cursor (%ld) %s", errval, errstr); + } + + return false; +} +#endif + static GdkGrabStatus do_pointer_grab(SpiceDisplay *display) { SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display); GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(display))); - GdkGrabStatus status; + GdkGrabStatus status = GDK_GRAB_BROKEN; GdkCursor *blank = get_blank_cursor(); if (!gtk_widget_get_realized(GTK_WIDGET(display))) - return GDK_GRAB_BROKEN; - try_keyboard_grab(display); + goto end; + +#ifdef WIN32 + if (!win32_clip_cursor()) + goto end; +#endif + try_keyboard_grab(display); /* * from gtk-vnc: * For relative mouse to work correctly when grabbed we need to @@ -761,22 +810,11 @@ static GdkGrabStatus do_pointer_grab(SpiceDisplay *display) d->mouse_grab_active = true; g_signal_emit(display, signals[SPICE_DISPLAY_MOUSE_GRAB], 0, true); } -#ifdef WIN32 - { - RECT client_rect; - POINT origin; - - origin.x = origin.y = 0; - ClientToScreen(focus_window, &origin); - GetClientRect(focus_window, &client_rect); - OffsetRect(&client_rect, origin.x, origin.y); - ClipCursor(&client_rect); - } -#endif if (status == GDK_GRAB_SUCCESS) set_mouse_accel(display, FALSE); +end: gdk_cursor_unref(blank); return status; } @@ -835,10 +873,21 @@ static void try_mouse_grab(SpiceDisplay *display) static void mouse_wrap(SpiceDisplay *display, GdkEventMotion *motion) { SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display); - GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(display)); + gint xr, yr; - gint xr = gdk_screen_get_width(screen) / 2; - gint yr = gdk_screen_get_height(screen) / 2; +#ifdef WIN32 + RECT clip; + g_return_if_fail(GetClipCursor(&clip)); + xr = clip.left + (clip.right - clip.left) / 2; + yr = clip.top + (clip.bottom - clip.top) / 2; + /* the clip rectangle has no offset, so we can't use gdk_wrap_pointer */ + SetCursorPos(xr, yr); + d->mouse_last_x = -1; + d->mouse_last_y = -1; +#else + GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(display)); + xr = gdk_screen_get_width(screen) / 2; + yr = gdk_screen_get_height(screen) / 2; if (xr != (gint)motion->x_root || yr != (gint)motion->y_root) { /* FIXME: we try our best to ignore that next pointer move event.. */ @@ -849,6 +898,8 @@ static void mouse_wrap(SpiceDisplay *display, GdkEventMotion *motion) d->mouse_last_x = -1; d->mouse_last_y = -1; } +#endif + } static void try_mouse_ungrab(SpiceDisplay *display) @@ -1409,7 +1460,8 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion) d->mouse_last_x = x; d->mouse_last_y = y; - mouse_wrap(display, motion); + if (dx != 0 || dy != 0) + mouse_wrap(display, motion); } break; default: -- 1.7.11.7 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel