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