[Spice-commits] 4 commits - gtk/spice-widget.c gtk/spice-widget-cairo.c

Marc-André Lureau elmarco at kemper.freedesktop.org
Sun Nov 18 04:55:51 PST 2012


 gtk/spice-widget-cairo.c |    1 
 gtk/spice-widget.c       |  122 ++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 95 insertions(+), 28 deletions(-)

New commits:
commit 1ed2038fbc3a2676d6e2333d20bf93f946f5472f
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Oct 31 16:39:27 2012 +0100

    win32: track current window handle
    
    We need current window handle for the global keyboard hook. It is not
    enough to rely on focus-in event to set it, so do it also in
    key-event. This avoids extra warnings on Windows.

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index fd4d9e6..c61a0c5 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -119,7 +119,7 @@ enum {
 static guint signals[SPICE_DISPLAY_LAST_SIGNAL];
 
 #ifdef WIN32
-static HWND focus_window = NULL;
+static HWND win32_window = NULL;
 #endif
 
 static void update_keyboard_grab(SpiceDisplay *display);
@@ -571,13 +571,13 @@ void spice_display_set_grab_keys(SpiceDisplay *display, SpiceGrabSequence *seq)
 #ifdef WIN32
 static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam)
 {
-    if  (focus_window && code == HC_ACTION) {
+    if  (win32_window && code == HC_ACTION) {
         KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT*)lparam;
         DWORD dwmsg = (hooked->flags << 24) | (hooked->scanCode << 16) | 1;
 
         if (hooked->vkCode == VK_NUMLOCK || hooked->vkCode == VK_RSHIFT) {
             dwmsg &= ~(1 << 24);
-            SendMessage(focus_window, wparam, hooked->vkCode, dwmsg);
+            SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
         }
         switch (hooked->vkCode) {
         case VK_CAPITAL:
@@ -591,7 +591,7 @@ static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam)
         case VK_RMENU:
             break;
         default:
-            SendMessage(focus_window, wparam, hooked->vkCode, dwmsg);
+            SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
             return 1;
         }
     }
@@ -1197,6 +1197,13 @@ static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval)
     return FALSE;
 }
 
+static void update_display(SpiceDisplay *display)
+{
+#ifdef WIN32
+    win32_window = display ? GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(display))) : NULL;
+#endif
+}
+
 static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
 {
     SpiceDisplay *display = SPICE_DISPLAY(widget);
@@ -1295,8 +1302,11 @@ static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
     SPICE_DEBUG("%s", __FUNCTION__);
+
     d->mouse_have_pointer = true;
     try_keyboard_grab(display);
+    update_display(display);
+
     return true;
 }
 
@@ -1334,10 +1344,8 @@ static gboolean focus_in_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UN
     sync_keyboard_lock_modifiers(display);
     update_keyboard_focus(display, true);
     try_keyboard_grab(display);
-#ifdef WIN32
-    focus_window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
-    g_return_val_if_fail(focus_window != NULL, true);
-#endif
+    update_display(display);
+
     return true;
 }
 
@@ -1347,6 +1355,7 @@ static gboolean focus_out_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_U
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
     SPICE_DEBUG("%s", __FUNCTION__);
+    update_display(NULL);
 
     /*
      * Ignore focus out after a keyboard grab
commit f0d04f1968658498f9dae75c3b3f30f62a799c33
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Nov 14 15:21:04 2012 +0100

    win32: clip and move cursor within window region
    
    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

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:
commit 8a7e72e3a22af8a46586e28cda96f977080f6589
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Nov 12 21:48:45 2012 +0100

    widget: regrab when widget is reconfigured
    
    On Windows, we need to update the cursor clip. Call ungrab&grab to update it.

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 588d028..1858499 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1520,6 +1520,10 @@ static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *conf)
         d->wh = conf->height;
         recalc_geometry(widget);
     }
+
+    try_mouse_ungrab(display);
+    try_mouse_grab(display);
+
     return true;
 }
 
commit 4ee7648b11877eb147af93fcece4920f4f44a08a
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Nov 12 21:22:26 2012 +0100

    widget: don't redraw server mouse until moved
    
    When switching between client mode and server mode, the pointer is
    being invalidated on each display and the cursor will end up being
    drawn on both. Since there is no information on which display the
    cursor is supposed to be until a move is received, hide the cursor
    until it actually moves.

diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index 9ddbe51..bd8e351 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -135,6 +135,7 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
         cairo_fill(cr);
 
         if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER &&
+            d->mouse_guest_x != -1 && d->mouse_guest_y != -1 &&
             !d->show_cursor) {
             GdkPixbuf *image = d->mouse_pixbuf;
             if (image != NULL) {
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index cdcff03..588d028 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1849,13 +1849,14 @@ static void update_mouse_mode(SpiceChannel *channel, gpointer data)
         break;
     case SPICE_MOUSE_MODE_SERVER:
         try_mouse_grab(display);
+        d->mouse_guest_x = -1;
+        d->mouse_guest_y = -1;
         break;
     default:
         g_warn_if_reached();
     }
 
     update_mouse_pointer(display);
-    cursor_invalidate(display);
 }
 
 static void update_area(SpiceDisplay *display,


More information about the Spice-commits mailing list