From 7d1e1499053472c9beec80e7abafd3c5083c3359 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Tue, 13 Aug 2013 12:57:41 +0200 Subject: [PATCH] Add MetaCursorTracker, a new helper for tracking the cursor sprite Under X, we need to use XFixes to watch the cursor changing, while on wayland, we're in charge of setting and painting the cursor. MetaCursorTracker provides the abstraction layer for gnome-shell, which can thus drop ShellXFixesCursor. In the future, it may grow the ability to watch for pointer position too, especially if CursorEvents are added to the next version of XInput2, and thus it would also replace the PointerWatcher we use for gnome-shell's magnifier. https://bugzilla.gnome.org/show_bug.cgi?id=705911 --- src/Makefile.am | 3 + src/core/display.c | 8 + src/core/meta-cursor-tracker-private.h | 34 +++ src/core/meta-cursor-tracker.c | 275 +++++++++++++++++++++++++ src/core/screen-private.h | 4 + src/core/screen.c | 35 ++-- src/meta/meta-cursor-tracker.h | 50 +++++ src/meta/types.h | 1 + 8 files changed, 395 insertions(+), 15 deletions(-) create mode 100644 src/core/meta-cursor-tracker-private.h create mode 100644 src/core/meta-cursor-tracker.c create mode 100644 src/meta/meta-cursor-tracker.h diff --git a/src/Makefile.am b/src/Makefile.am index a5e556223..638a3cddb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -113,6 +113,8 @@ libmutter_la_SOURCES = \ core/keybindings.c \ core/keybindings-private.h \ core/main.c \ + core/meta-cursor-tracker.c \ + core/meta-cursor-tracker-private.h \ core/meta-xrandr-shared.h \ core/monitor.c \ core/monitor-config.c \ @@ -186,6 +188,7 @@ libmutterinclude_base_headers = \ meta/meta-background-actor.h \ meta/meta-background-group.h \ meta/meta-background.h \ + meta/meta-cursor-tracker.h \ meta/meta-plugin.h \ meta/meta-shaped-texture.h \ meta/meta-shadow-factory.h \ diff --git a/src/core/display.c b/src/core/display.c index 64f5511c1..e192d3f19 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2147,6 +2147,7 @@ event_callback (XEvent *event, gboolean filter_out_event; XIEvent *input_event; MetaMonitorManager *monitor; + MetaScreen *screen; display = data; @@ -2184,6 +2185,13 @@ event_callback (XEvent *event, display->server_focus_serial); } + screen = meta_display_screen_for_root (display, event->xany.window); + if (screen) + { + if (meta_screen_handle_xevent (screen, event)) + return TRUE; + } + modified = event_get_modified_window (display, event); input_event = get_input_event (display, event); diff --git a/src/core/meta-cursor-tracker-private.h b/src/core/meta-cursor-tracker-private.h new file mode 100644 index 000000000..c679b01ac --- /dev/null +++ b/src/core/meta-cursor-tracker-private.h @@ -0,0 +1,34 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Giovanni Campagna + */ + +#ifndef META_CURSOR_TRACKER_PRIVATE_H +#define META_CURSOR_TRACKER_PRIVATE_H + +#include + +gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker, + XEvent *xevent); + +void meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker, + MetaCursor cursor); +#endif diff --git a/src/core/meta-cursor-tracker.c b/src/core/meta-cursor-tracker.c new file mode 100644 index 000000000..c51a827de --- /dev/null +++ b/src/core/meta-cursor-tracker.c @@ -0,0 +1,275 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Giovanni Campagna + */ + +/** + * SECTION:cursor-tracker + * @title: MetaCursorTracker + * @short_description: Mutter cursor tracking helper + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "meta-cursor-tracker-private.h" +#include "screen-private.h" + +#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7 +#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4 + +struct _MetaCursorTracker { + GObject parent_instance; + + MetaScreen *screen; + + gboolean is_showing; + + CoglTexture2D *sprite; + int hot_x, hot_y; +}; + +struct _MetaCursorTrackerClass { + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT); + +enum { + CURSOR_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static void +meta_cursor_tracker_init (MetaCursorTracker *self) +{ + /* (JS) Best (?) that can be assumed since XFixes doesn't provide a way of + * detecting if the system mouse cursor is showing or not. + * + * On wayland we start with the cursor showing + */ + self->is_showing = TRUE; +} + +static void +meta_cursor_tracker_finalize (GObject *object) +{ + MetaCursorTracker *self = META_CURSOR_TRACKER (object); + + if (self->sprite) + cogl_object_unref (self->sprite); + + G_OBJECT_CLASS (meta_cursor_tracker_parent_class)->finalize (object); +} + +static void +meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_cursor_tracker_finalize; + + signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static MetaCursorTracker * +make_x11_cursor_tracker (MetaScreen *screen) +{ + MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL); + self->screen = screen; + + XFixesSelectCursorInput (screen->display->xdisplay, + screen->xroot, + XFixesDisplayCursorNotifyMask); + + return self; +} + +/** + * meta_cursor_tracker_get_for_screen: + * @screen: the #MetaScreen + * + * Retrieves the cursor tracker object for @screen. + * + * Returns: (transfer none): + */ +MetaCursorTracker * +meta_cursor_tracker_get_for_screen (MetaScreen *screen) +{ + MetaCursorTracker *self; + + if (screen->cursor_tracker) + return screen->cursor_tracker; + + self = make_x11_cursor_tracker (screen); + + screen->cursor_tracker = self; + return self; +} + +gboolean +meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker, + XEvent *xevent) +{ + XFixesCursorNotifyEvent *notify_event; + + if (xevent->xany.type != tracker->screen->display->xfixes_event_base + XFixesCursorNotify) + return FALSE; + + notify_event = (XFixesCursorNotifyEvent *)xevent; + if (notify_event->subtype != XFixesDisplayCursorNotify) + return FALSE; + + g_clear_pointer (&tracker->sprite, cogl_object_unref); + g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); + + return TRUE; +} + +static void +ensure_xfixes_cursor (MetaCursorTracker *tracker) +{ + XFixesCursorImage *cursor_image; + CoglTexture2D *sprite; + guint8 *cursor_data; + gboolean free_cursor_data; + CoglContext *ctx; + + if (tracker->sprite) + return; + + cursor_image = XFixesGetCursorImage (tracker->screen->display->xdisplay); + if (!cursor_image) + return; + + /* Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit + * quantities as arrays of long; we need to convert on 64 bit */ + if (sizeof(long) == 4) + { + cursor_data = (guint8 *)cursor_image->pixels; + free_cursor_data = FALSE; + } + else + { + int i, j; + guint32 *cursor_words; + gulong *p; + guint32 *q; + + cursor_words = g_new (guint32, cursor_image->width * cursor_image->height); + cursor_data = (guint8 *)cursor_words; + + p = cursor_image->pixels; + q = cursor_words; + for (j = 0; j < cursor_image->height; j++) + for (i = 0; i < cursor_image->width; i++) + *(q++) = *(p++); + + free_cursor_data = TRUE; + } + + ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); + sprite = cogl_texture_2d_new_from_data (ctx, + cursor_image->width, + cursor_image->height, + CLUTTER_CAIRO_FORMAT_ARGB32, + COGL_PIXEL_FORMAT_ANY, + cursor_image->width * 4, /* stride */ + cursor_data, + NULL); + + if (free_cursor_data) + g_free (cursor_data); + + if (sprite != NULL) + { + tracker->sprite = sprite; + tracker->hot_x = cursor_image->xhot; + tracker->hot_y = cursor_image->yhot; + } + XFree (cursor_image); +} + +/** + * meta_cursor_tracker_get_sprite: + * + * Returns: (transfer none): + */ +CoglTexture * +meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker) +{ + g_return_val_if_fail (META_IS_CURSOR_TRACKER (tracker), NULL); + + ensure_xfixes_cursor (tracker); + + return COGL_TEXTURE (tracker->sprite); +} + +/** + * meta_cursor_tracker_get_hot: + * @tracker: + * @x: (out): + * @y: (out): + * + */ +void +meta_cursor_tracker_get_hot (MetaCursorTracker *tracker, + int *x, + int *y) +{ + g_return_if_fail (META_IS_CURSOR_TRACKER (tracker)); + + ensure_xfixes_cursor (tracker); + + if (x) + *x = tracker->hot_x; + if (y) + *y = tracker->hot_y; +} + +void +meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker, + MetaCursor cursor) +{ + Cursor xcursor; + MetaDisplay *display = tracker->screen->display; + + /* First create a cursor for X11 applications that don't specify their own */ + xcursor = meta_display_create_x_cursor (display, cursor); + + XDefineCursor (display->xdisplay, tracker->screen->xroot, xcursor); + XFlush (display->xdisplay); + XFreeCursor (display->xdisplay, xcursor); +} diff --git a/src/core/screen-private.h b/src/core/screen-private.h index 9e0da8d7a..5d7bfd0aa 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -83,6 +83,7 @@ struct _MetaScreen MetaStack *stack; MetaStackTracker *stack_tracker; + MetaCursorTracker *cursor_tracker; MetaCursor current_cursor; Window flash_window; @@ -249,4 +250,7 @@ int meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen, int meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen, int index); +gboolean meta_screen_handle_xevent (MetaScreen *screen, + XEvent *xevent); + #endif diff --git a/src/core/screen.c b/src/core/screen.c index 1c643dca5..c947f0e09 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -45,6 +45,7 @@ #include #include "mutter-enum-types.h" #include "core.h" +#include "meta-cursor-tracker-private.h" #include @@ -705,7 +706,8 @@ meta_screen_new (MetaDisplay *display, screen->guard_window = None; reload_monitor_infos (screen); - + + meta_cursor_tracker_get_for_screen (screen); meta_screen_set_cursor (screen, META_CURSOR_DEFAULT); /* Handle creating a no_focus_window for this screen */ @@ -1461,29 +1463,18 @@ void meta_screen_set_cursor (MetaScreen *screen, MetaCursor cursor) { - Cursor xcursor; - if (cursor == screen->current_cursor) return; screen->current_cursor = cursor; - - xcursor = meta_display_create_x_cursor (screen->display, cursor); - XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor); - XFlush (screen->display->xdisplay); - XFreeCursor (screen->display->xdisplay, xcursor); + meta_cursor_tracker_set_root_cursor (screen->cursor_tracker, cursor); } void meta_screen_update_cursor (MetaScreen *screen) { - Cursor xcursor; - - xcursor = meta_display_create_x_cursor (screen->display, - screen->current_cursor); - XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor); - XFlush (screen->display->xdisplay); - XFreeCursor (screen->display->xdisplay, xcursor); + meta_cursor_tracker_set_root_cursor (screen->cursor_tracker, + screen->current_cursor); } void @@ -3688,3 +3679,17 @@ meta_screen_get_monitor_in_fullscreen (MetaScreen *screen, /* We use -1 as a flag to mean "not known yet" for notification purposes */ return screen->monitor_infos[monitor].in_fullscreen == TRUE; } + +gboolean +meta_screen_handle_xevent (MetaScreen *screen, + XEvent *xevent) +{ + /* Go through our helpers and see if they want this event. + Currently, only MetaCursorTracker. + */ + + if (meta_cursor_tracker_handle_xevent (screen->cursor_tracker, xevent)) + return TRUE; + + return FALSE; +} diff --git a/src/meta/meta-cursor-tracker.h b/src/meta/meta-cursor-tracker.h new file mode 100644 index 000000000..c59c6cefb --- /dev/null +++ b/src/meta/meta-cursor-tracker.h @@ -0,0 +1,50 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Giovanni Campagna + */ + +#ifndef META_CURSOR_TRACKER_H +#define META_CURSOR_TRACKER_H + +#include +#include +#include +#include + +#define META_TYPE_CURSOR_TRACKER (meta_cursor_tracker_get_type ()) +#define META_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTracker)) +#define META_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass)) +#define META_IS_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_TRACKER)) +#define META_IS_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_TRACKER)) +#define META_CURSOR_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass)) + +typedef struct _MetaCursorTrackerClass MetaCursorTrackerClass; + +GType meta_cursor_tracker_get_type (void); + +MetaCursorTracker *meta_cursor_tracker_get_for_screen (MetaScreen *screen); + +void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker, + int *x, + int *y); +CoglTexture *meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker); + +#endif diff --git a/src/meta/types.h b/src/meta/types.h index 25833207d..f13471146 100644 --- a/src/meta/types.h +++ b/src/meta/types.h @@ -38,5 +38,6 @@ typedef struct _MetaWorkspace MetaWorkspace; */ typedef struct _MetaGroup MetaGroup; typedef struct _MetaKeyBinding MetaKeyBinding; +typedef struct _MetaCursorTracker MetaCursorTracker; #endif