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

Alon Levy alevy at redhat.com
Thu Nov 15 13:27:07 PST 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).
> 
> 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;

silly question: why xr? what does r stand for? It appears to be the center of the clip region, but I can't figure out the name.

>  
> -    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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
> 


More information about the Spice-devel mailing list