[Spice-devel] [PATCH spice-gtk 3/3] win32: clip and move cursor within window region

Marc-André Lureau marcandre.lureau at gmail.com
Wed Oct 31 08:51:05 PDT 2012


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).

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 | 69 ++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 51 insertions(+), 18 deletions(-)

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 023ec5e..63f8425 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -599,17 +599,57 @@ static void update_keyboard_grab(SpiceDisplay *display)
         try_keyboard_ungrab(display);
 }
 
+#ifdef WIN32
+static gboolean win32_clip_cursor(void)
+{
+    RECT window, workarea, rect;
+
+    g_return_val_if_fail(win32_window != NULL, FALSE);
+
+    if (!GetWindowRect(win32_window, &window))
+        goto error;
+
+    if (!SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea, 0))
+        goto error;
+
+    if (!IntersectRect(&rect, &window, &workarea))
+        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
@@ -635,18 +675,6 @@ 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
 
 #ifdef GDK_WINDOWING_X11
     if (status == GDK_GRAB_SUCCESS) {
@@ -663,6 +691,7 @@ static GdkGrabStatus do_pointer_grab(SpiceDisplay *display)
     }
 #endif
 
+end:
     gdk_cursor_unref(blank);
     return status;
 }
@@ -722,10 +751,14 @@ 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;
+#endif
     if (xr != (gint)motion->x_root || yr != (gint)motion->y_root) {
         /* FIXME: we try our best to ignore that next pointer move event.. */
         gdk_display_sync(gdk_screen_get_display(screen));
-- 
1.7.11.7



More information about the Spice-devel mailing list