[PATCH spice-gtk] win32: clip and move cursor within window region

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]