Support scaling of cursor sprites given what output they are on

This commits refactors cursor handling code and plugs in logic so that
cursor sprites changes appearance as it moves across the screen.
Renderers are adapted to handle the necessary functionality.

The logic for changing the cursor sprite appearance is done outside of
MetaCursorSprite, and actually where depends on what type of cursor it
is. In mutter we now have two types of cursors that may have their
appearance changed:

 - Themed cursors (aka root cursors)
 - wl_surface cursors

Themed cursors are created by MetaScreen and when created, when
applicable(*), it will extend the cursor via connecting to a signal
which is emitted everytime the cursor is moved. The signal handler will
calculate the expected scale given the monitor it is on and reload the
theme in a correct size when needed.

wl_surface cursors are created when a wl_surface is assigned the
"cursor" role, i.e. when a client calls wl_pointer.set_cursor. A
cursor role object is created which is connected to the cursor object
by the position signal, and will set a correct texture scale given what
monitor the cursor is on and what scale the wl_surface's active buffer
is in. It will also push new buffers to the same to the cursor object
when new ones are committed to the surface.

This commit also makes texture loading lazy, since the renderer doesn't
calculate a rectangle when the cursor position changes.

The native backend is refactored to be triple-buffered; see the comment
in meta-cursor-renderer-native.c for further explanations.

* when we are running as a Wayland compositor

https://bugzilla.gnome.org/show_bug.cgi?id=744932
This commit is contained in:
Jonas Ådahl 2015-07-17 23:16:39 +08:00
parent 7c7cf91c32
commit 79c86ae890
13 changed files with 634 additions and 249 deletions

View File

@ -37,7 +37,6 @@
struct _MetaCursorRendererPrivate struct _MetaCursorRendererPrivate
{ {
int current_x, current_y; int current_x, current_y;
MetaRectangle current_rect;
MetaCursorSprite *displayed_cursor; MetaCursorSprite *displayed_cursor;
gboolean handled_by_backend; gboolean handled_by_backend;
@ -47,28 +46,33 @@ typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT);
static void static void
queue_redraw (MetaCursorRenderer *renderer) queue_redraw (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend (); MetaBackend *backend = meta_get_backend ();
ClutterActor *stage = meta_backend_get_stage (backend); ClutterActor *stage = meta_backend_get_stage (backend);
CoglTexture *texture; CoglTexture *texture;
MetaRectangle rect = { 0 };
if (cursor_sprite)
rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
/* During early initialization, we can have no stage */ /* During early initialization, we can have no stage */
if (!stage) if (!stage)
return; return;
if (priv->displayed_cursor && !priv->handled_by_backend) if (cursor_sprite && !priv->handled_by_backend)
texture = meta_cursor_sprite_get_cogl_texture (priv->displayed_cursor, texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
NULL, NULL);
else else
texture = NULL; texture = NULL;
meta_stage_set_cursor (META_STAGE (stage), texture, &priv->current_rect); meta_stage_set_cursor (META_STAGE (stage), texture, &rect);
} }
static gboolean static gboolean
meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer) meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
return FALSE; return FALSE;
} }
@ -84,35 +88,50 @@ meta_cursor_renderer_init (MetaCursorRenderer *renderer)
{ {
} }
MetaRectangle
meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{
MetaCursorRendererPrivate *priv =
meta_cursor_renderer_get_instance_private (renderer);
CoglTexture *texture;
int hot_x, hot_y;
int width, height;
float texture_scale;
texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
if (!texture)
return (MetaRectangle) { 0 };
meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y);
texture_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
width = cogl_texture_get_width (texture);
height = cogl_texture_get_height (texture);
return (MetaRectangle) {
.x = (int)roundf (priv->current_x - (hot_x * texture_scale)),
.y = (int)roundf (priv->current_y - (hot_y * texture_scale)),
.width = (int)roundf (width * texture_scale),
.height = (int)roundf (height * texture_scale),
};
}
static void static void
update_cursor (MetaCursorRenderer *renderer) update_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
gboolean handled_by_backend; gboolean handled_by_backend;
gboolean should_redraw = FALSE; gboolean should_redraw = FALSE;
if (priv->displayed_cursor) if (cursor_sprite)
{ meta_cursor_sprite_prepare_at (cursor_sprite,
CoglTexture *texture; priv->current_x,
int hot_x, hot_y; priv->current_y);
texture = meta_cursor_sprite_get_cogl_texture (priv->displayed_cursor, handled_by_backend =
&hot_x, &hot_y); META_CURSOR_RENDERER_GET_CLASS (renderer)->update_cursor (renderer,
cursor_sprite);
priv->current_rect.x = priv->current_x - hot_x;
priv->current_rect.y = priv->current_y - hot_y;
priv->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (texture));
priv->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (texture));
}
else
{
priv->current_rect.x = 0;
priv->current_rect.y = 0;
priv->current_rect.width = 0;
priv->current_rect.height = 0;
}
handled_by_backend = META_CURSOR_RENDERER_GET_CLASS (renderer)->update_cursor (renderer);
if (handled_by_backend != priv->handled_by_backend) if (handled_by_backend != priv->handled_by_backend)
{ {
priv->handled_by_backend = handled_by_backend; priv->handled_by_backend = handled_by_backend;
@ -123,7 +142,7 @@ update_cursor (MetaCursorRenderer *renderer)
should_redraw = TRUE; should_redraw = TRUE;
if (should_redraw) if (should_redraw)
queue_redraw (renderer); queue_redraw (renderer, cursor_sprite);
} }
MetaCursorRenderer * MetaCursorRenderer *
@ -140,16 +159,18 @@ meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer,
if (priv->displayed_cursor == cursor_sprite) if (priv->displayed_cursor == cursor_sprite)
return; return;
priv->displayed_cursor = cursor_sprite; priv->displayed_cursor = cursor_sprite;
update_cursor (renderer);
update_cursor (renderer, cursor_sprite);
} }
void void
meta_cursor_renderer_force_update (MetaCursorRenderer *renderer) meta_cursor_renderer_force_update (MetaCursorRenderer *renderer)
{ {
update_cursor (renderer); MetaCursorRendererPrivate *priv =
queue_redraw (renderer); meta_cursor_renderer_get_instance_private (renderer);
update_cursor (renderer, priv->displayed_cursor);
} }
void void
@ -163,7 +184,7 @@ meta_cursor_renderer_set_position (MetaCursorRenderer *renderer,
priv->current_x = x; priv->current_x = x;
priv->current_y = y; priv->current_y = y;
update_cursor (renderer); update_cursor (renderer, priv->displayed_cursor);
} }
MetaCursorSprite * MetaCursorSprite *
@ -174,14 +195,6 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
return priv->displayed_cursor; return priv->displayed_cursor;
} }
const MetaRectangle *
meta_cursor_renderer_get_rect (MetaCursorRenderer *renderer)
{
MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
return &priv->current_rect;
}
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
void void
meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer, meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,

View File

@ -42,7 +42,8 @@ struct _MetaCursorRendererClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
gboolean (* update_cursor) (MetaCursorRenderer *renderer); gboolean (* update_cursor) (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite);
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
void (* realize_cursor_from_wl_buffer) (MetaCursorRenderer *renderer, void (* realize_cursor_from_wl_buffer) (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite, MetaCursorSprite *cursor_sprite,
@ -65,7 +66,9 @@ void meta_cursor_renderer_set_position (MetaCursorRenderer *renderer,
void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer); void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer);
MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer); MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer);
const MetaRectangle * meta_cursor_renderer_get_rect (MetaCursorRenderer *renderer);
MetaRectangle meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite);
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer, void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,

View File

@ -246,10 +246,11 @@ ensure_xfixes_cursor (MetaCursorTracker *tracker)
if (sprite != NULL) if (sprite != NULL)
{ {
MetaCursorSprite *cursor_sprite = MetaCursorSprite *cursor_sprite = meta_cursor_sprite_new ();
meta_cursor_sprite_from_texture (sprite, meta_cursor_sprite_set_texture (cursor_sprite,
cursor_image->xhot, sprite,
cursor_image->yhot); cursor_image->xhot,
cursor_image->yhot);
cogl_object_unref (sprite); cogl_object_unref (sprite);
tracker->xfixes_cursor = cursor_sprite; tracker->xfixes_cursor = cursor_sprite;
} }
@ -279,7 +280,7 @@ meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
} }
if (cursor_sprite) if (cursor_sprite)
return meta_cursor_sprite_get_cogl_texture (cursor_sprite, NULL, NULL); return meta_cursor_sprite_get_cogl_texture (cursor_sprite);
else else
return NULL; return NULL;
} }
@ -311,7 +312,7 @@ meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
} }
if (cursor_sprite) if (cursor_sprite)
meta_cursor_sprite_get_cogl_texture (cursor_sprite, x, y); meta_cursor_sprite_get_hotspot (cursor_sprite, x, y);
else else
{ {
if (x) if (x)

View File

@ -35,9 +35,13 @@
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <X11/Xcursor/Xcursor.h> #include <X11/Xcursor/Xcursor.h>
#ifdef HAVE_WAYLAND enum {
#include <cogl/cogl-wayland-server.h> PREPARE_AT,
#endif
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
struct _MetaCursorSprite struct _MetaCursorSprite
{ {
@ -46,10 +50,14 @@ struct _MetaCursorSprite
MetaCursor cursor; MetaCursor cursor;
CoglTexture2D *texture; CoglTexture2D *texture;
float texture_scale;
int hot_x, hot_y; int hot_x, hot_y;
int current_frame; int current_frame;
XcursorImages *xcursor_images; XcursorImages *xcursor_images;
int theme_scale;
gboolean theme_dirty;
}; };
GType meta_cursor_sprite_get_type (void) G_GNUC_CONST; GType meta_cursor_sprite_get_type (void) G_GNUC_CONST;
@ -112,11 +120,11 @@ meta_cursor_create_x_cursor (Display *xdisplay,
} }
static XcursorImages * static XcursorImages *
load_cursor_on_client (MetaCursor cursor) load_cursor_on_client (MetaCursor cursor, int scale)
{ {
return XcursorLibraryLoadImages (translate_meta_cursor (cursor), return XcursorLibraryLoadImages (translate_meta_cursor (cursor),
meta_prefs_get_cursor_theme (), meta_prefs_get_cursor_theme (),
meta_prefs_get_cursor_size ()); meta_prefs_get_cursor_size () * scale);
} }
static void static void
@ -129,6 +137,7 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
CoglPixelFormat cogl_format; CoglPixelFormat cogl_format;
ClutterBackend *clutter_backend; ClutterBackend *clutter_backend;
CoglContext *cogl_context; CoglContext *cogl_context;
CoglTexture *texture;
g_assert (self->texture == NULL); g_assert (self->texture == NULL);
@ -144,14 +153,15 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
clutter_backend = clutter_get_default_backend (); clutter_backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (clutter_backend); cogl_context = clutter_backend_get_cogl_context (clutter_backend);
self->texture = cogl_texture_2d_new_from_data (cogl_context, texture = cogl_texture_2d_new_from_data (cogl_context,
width, height, width, height,
cogl_format, cogl_format,
rowstride, rowstride,
(uint8_t *) xc_image->pixels, (uint8_t *) xc_image->pixels,
NULL); NULL);
self->hot_x = xc_image->xhot; meta_cursor_sprite_set_texture (self, texture,
self->hot_y = xc_image->yhot; xc_image->xhot, xc_image->yhot);
cogl_object_unref (texture);
meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image); meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image);
} }
@ -198,92 +208,82 @@ meta_cursor_sprite_is_animated (MetaCursorSprite *self)
} }
MetaCursorSprite * MetaCursorSprite *
meta_cursor_sprite_from_theme (MetaCursor cursor) meta_cursor_sprite_new (void)
{
return g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
}
static void
meta_cursor_sprite_load_from_theme (MetaCursorSprite *self)
{ {
MetaCursorSprite *self;
XcursorImage *image; XcursorImage *image;
self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL); g_assert (self->cursor != META_CURSOR_NONE);
/* We might be reloading with a different scale. If so clear the old data. */
if (self->xcursor_images)
{
g_clear_pointer (&self->texture, cogl_object_unref);
XcursorImagesDestroy (self->xcursor_images);
}
self->cursor = cursor;
self->current_frame = 0; self->current_frame = 0;
self->xcursor_images = load_cursor_on_client (self->cursor); self->xcursor_images = load_cursor_on_client (self->cursor,
self->theme_scale);
if (!self->xcursor_images) if (!self->xcursor_images)
meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?"); meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?");
image = meta_cursor_sprite_get_current_frame_image (self); image = meta_cursor_sprite_get_current_frame_image (self);
meta_cursor_sprite_load_from_xcursor_image (self, image); meta_cursor_sprite_load_from_xcursor_image (self, image);
return self; self->theme_dirty = FALSE;
} }
MetaCursorSprite * MetaCursorSprite *
meta_cursor_sprite_from_texture (CoglTexture2D *texture, meta_cursor_sprite_from_theme (MetaCursor cursor)
int hot_x,
int hot_y)
{ {
MetaCursorSprite *self; MetaCursorSprite *self;
self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL); self = meta_cursor_sprite_new ();
cogl_object_ref (texture); self->cursor = cursor;
self->theme_dirty = TRUE;
self->texture = texture;
self->hot_x = hot_x;
self->hot_y = hot_y;
return self; return self;
} }
#ifdef HAVE_WAYLAND void
static void meta_cursor_sprite_set_texture (MetaCursorSprite *self,
meta_cursor_sprite_load_from_buffer (MetaCursorSprite *self, CoglTexture *texture,
struct wl_resource *buffer, int hot_x,
int hot_x, int hot_y)
int hot_y)
{ {
MetaBackend *meta_backend = meta_get_backend (); g_clear_pointer (&self->texture, cogl_object_unref);
MetaCursorRenderer *renderer = if (texture)
meta_backend_get_cursor_renderer (meta_backend); self->texture = cogl_object_ref (texture);
ClutterBackend *backend;
CoglContext *cogl_context;
self->hot_x = hot_x; self->hot_x = hot_x;
self->hot_y = hot_y; self->hot_y = hot_y;
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
self->texture = cogl_wayland_texture_2d_new_from_buffer (cogl_context, buffer, NULL);
meta_cursor_renderer_realize_cursor_from_wl_buffer (renderer, self, buffer);
} }
MetaCursorSprite * void
meta_cursor_sprite_from_buffer (struct wl_resource *buffer, meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
int hot_x, float scale)
int hot_y)
{ {
MetaCursorSprite *self; self->texture_scale = scale;
}
self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
void
meta_cursor_sprite_load_from_buffer (self, buffer, hot_x, hot_y); meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
int theme_scale)
return self; {
if (self->theme_scale != theme_scale)
self->theme_dirty = TRUE;
self->theme_scale = theme_scale;
} }
#endif
CoglTexture * CoglTexture *
meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self, meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self)
int *hot_x,
int *hot_y)
{ {
if (hot_x)
*hot_x = self->hot_x;
if (hot_y)
*hot_y = self->hot_y;
return COGL_TEXTURE (self->texture); return COGL_TEXTURE (self->texture);
} }
@ -302,21 +302,31 @@ meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
*hot_y = self->hot_y; *hot_y = self->hot_y;
} }
guint float
meta_cursor_sprite_get_width (MetaCursorSprite *self) meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self)
{ {
return cogl_texture_get_width (COGL_TEXTURE (self->texture)); return self->texture_scale;
} }
guint void
meta_cursor_sprite_get_height (MetaCursorSprite *self) meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
int x,
int y)
{ {
return cogl_texture_get_height (COGL_TEXTURE (self->texture)); g_signal_emit (self, signals[PREPARE_AT], 0, x, y);
}
void
meta_cursor_sprite_realize_texture (MetaCursorSprite *self)
{
if (self->theme_dirty)
meta_cursor_sprite_load_from_theme (self);
} }
static void static void
meta_cursor_sprite_init (MetaCursorSprite *self) meta_cursor_sprite_init (MetaCursorSprite *self)
{ {
self->texture_scale = 1.0f;
} }
static void static void
@ -338,4 +348,13 @@ meta_cursor_sprite_class_init (MetaCursorSpriteClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_cursor_sprite_finalize; object_class->finalize = meta_cursor_sprite_finalize;
signals[PREPARE_AT] = g_signal_new ("prepare-at",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_INT);
} }

View File

@ -23,6 +23,7 @@
#define META_CURSOR_H #define META_CURSOR_H
#include <meta/common.h> #include <meta/common.h>
#include <meta/boxes.h>
typedef struct _MetaCursorSprite MetaCursorSprite; typedef struct _MetaCursorSprite MetaCursorSprite;
@ -32,35 +33,40 @@ G_DECLARE_FINAL_TYPE (MetaCursorSprite,
META, CURSOR_SPRITE, META, CURSOR_SPRITE,
GObject); GObject);
MetaCursorSprite * meta_cursor_sprite_from_theme (MetaCursor cursor); MetaCursorSprite * meta_cursor_sprite_new (void);
#ifdef HAVE_WAYLAND MetaCursorSprite * meta_cursor_sprite_from_theme (MetaCursor cursor);
#include <wayland-server.h>
MetaCursorSprite * meta_cursor_sprite_from_buffer (struct wl_resource *buffer,
int hot_x, void meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
int hot_y); int scale);
#endif
MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self); MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self);
MetaCursorSprite * meta_cursor_sprite_from_texture (CoglTexture2D *texture,
int hot_x,
int hot_y);
Cursor meta_cursor_create_x_cursor (Display *xdisplay, Cursor meta_cursor_create_x_cursor (Display *xdisplay,
MetaCursor cursor); MetaCursor cursor);
CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self, void meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
int *hot_x, int x,
int *hot_y); int y);
void meta_cursor_sprite_realize_texture (MetaCursorSprite *self);
void meta_cursor_sprite_set_texture (MetaCursorSprite *self,
CoglTexture *texture,
int hot_x,
int hot_y);
void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
float scale);
CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self);
void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self, void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
int *hot_x, int *hot_x,
int *hot_y); int *hot_y);
guint meta_cursor_sprite_get_width (MetaCursorSprite *self); float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self);
guint meta_cursor_sprite_get_height (MetaCursorSprite *self);
gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *self); gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *self);
void meta_cursor_sprite_tick_frame (MetaCursorSprite *self); void meta_cursor_sprite_tick_frame (MetaCursorSprite *self);

View File

@ -174,7 +174,8 @@ struct _MetaCRTC
/* Used when changing configuration */ /* Used when changing configuration */
gboolean is_dirty; gboolean is_dirty;
MetaCursorSprite *cursor; /* Used by cursor renderer backend */
void *cursor_renderer_private;
gpointer driver_private; gpointer driver_private;
GDestroyNotify driver_notify; GDestroyNotify driver_notify;

View File

@ -29,9 +29,13 @@
#include <string.h> #include <string.h>
#include <gbm.h> #include <gbm.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <errno.h>
#include <meta/util.h> #include <meta/util.h>
#include <meta/meta-backend.h>
#include "meta-monitor-manager-private.h" #include "meta-monitor-manager-private.h"
#include "meta/boxes.h"
#ifndef DRM_CAP_CURSOR_WIDTH #ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8 #define DRM_CAP_CURSOR_WIDTH 0x8
@ -40,6 +44,19 @@
#define DRM_CAP_CURSOR_HEIGHT 0x9 #define DRM_CAP_CURSOR_HEIGHT 0x9
#endif #endif
/* When animating a cursor, we usually call drmModeSetCursor2 once per frame.
* Though, testing shows that we need to triple buffer the cursor buffer in
* order to avoid glitches when animating the cursor, at least when running on
* Intel. The reason for this might be (but is not confirmed to be) due to
* the user space gbm_bo cache, making us reuse and overwrite the kernel side
* buffer content before it was scanned out. To avoid this, we keep a user space
* reference to each buffer we set until at least one frame after it was drawn.
* In effect, this means we three active cursor gbm_bo's: one that that just has
* been set, one that was previously set and may or may not have been scanned
* out, and one pending that will be replaced if the cursor sprite changes.
*/
#define HW_CURSOR_BUFFER_COUNT 3
static GQuark quark_cursor_sprite = 0; static GQuark quark_cursor_sprite = 0;
struct _MetaCursorRendererNativePrivate struct _MetaCursorRendererNativePrivate
@ -57,8 +74,25 @@ struct _MetaCursorRendererNativePrivate
}; };
typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate; typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate;
typedef enum _MetaCursorGbmBoState
{
META_CURSOR_GBM_BO_STATE_NONE,
META_CURSOR_GBM_BO_STATE_SET,
META_CURSOR_GBM_BO_STATE_INVALIDATED,
} MetaCursorGbmBoState;
typedef struct _MetaCursorNativePrivate
{
guint active_bo;
MetaCursorGbmBoState pending_bo_state;
struct gbm_bo *bos[HW_CURSOR_BUFFER_COUNT];
} MetaCursorNativePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, META_TYPE_CURSOR_RENDERER); G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, META_TYPE_CURSOR_RENDERER);
static MetaCursorNativePrivate *
ensure_cursor_priv (MetaCursorSprite *cursor_sprite);
static void static void
meta_cursor_renderer_native_finalize (GObject *object) meta_cursor_renderer_native_finalize (GObject *object)
{ {
@ -74,26 +108,53 @@ meta_cursor_renderer_native_finalize (GObject *object)
G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object); G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
} }
static guint
get_pending_cursor_sprite_gbm_bo_index (MetaCursorSprite *cursor_sprite)
{
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
return (cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT;
}
static struct gbm_bo * static struct gbm_bo *
get_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite) get_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
{ {
return g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
guint pending_bo;
if (!cursor_priv)
return NULL;
pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
return cursor_priv->bos[pending_bo];
}
static struct gbm_bo *
get_active_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
{
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
if (!cursor_priv)
return NULL;
return cursor_priv->bos[cursor_priv->active_bo];
} }
static void static void
cursor_gbm_bo_free (gpointer data) set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite,
struct gbm_bo *bo)
{ {
if (!data) MetaCursorNativePrivate *cursor_priv;
return; guint pending_bo;
gbm_bo_destroy ((struct gbm_bo *)data); cursor_priv = ensure_cursor_priv (cursor_sprite);
}
static void pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
set_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, struct gbm_bo *bo) cursor_priv->bos[pending_bo] = bo;
{ cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_SET;
g_object_set_qdata_full (G_OBJECT (cursor_sprite), quark_cursor_sprite,
bo, cursor_gbm_bo_free);
} }
static void static void
@ -104,55 +165,78 @@ set_crtc_cursor (MetaCursorRendererNative *native,
{ {
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
if (crtc->cursor == cursor_sprite && !force)
return;
crtc->cursor = cursor_sprite;
if (cursor_sprite) if (cursor_sprite)
{ {
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
struct gbm_bo *bo; struct gbm_bo *bo;
union gbm_bo_handle handle; union gbm_bo_handle handle;
int hot_x, hot_y; int hot_x, hot_y;
bo = get_cursor_sprite_gbm_bo (cursor_sprite); if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET)
bo = get_pending_cursor_sprite_gbm_bo (cursor_sprite);
else
bo = get_active_cursor_sprite_gbm_bo (cursor_sprite);
if (!force && bo == crtc->cursor_renderer_private)
return;
crtc->cursor_renderer_private = bo;
handle = gbm_bo_get_handle (bo); handle = gbm_bo_get_handle (bo);
meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y); meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y);
drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32, drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32,
priv->cursor_width, priv->cursor_height, hot_x, hot_y); priv->cursor_width, priv->cursor_height, hot_x, hot_y);
if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET)
{
cursor_priv->active_bo =
(cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT;
cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_NONE;
}
} }
else else
{ {
drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0); if (force || crtc->cursor_renderer_private != NULL)
{
drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0);
crtc->cursor_renderer_private = NULL;
}
} }
} }
static void static void
update_hw_cursor (MetaCursorRendererNative *native, update_hw_cursor (MetaCursorRendererNative *native,
MetaCursorSprite *cursor_sprite,
gboolean force) gboolean force)
{ {
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native); MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
const MetaRectangle *cursor_rect = meta_cursor_renderer_get_rect (renderer);
MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
MetaMonitorManager *monitors; MetaMonitorManager *monitors;
MetaCRTC *crtcs; MetaCRTC *crtcs;
unsigned int i, n_crtcs; unsigned int i, n_crtcs;
MetaRectangle rect;
monitors = meta_monitor_manager_get (); monitors = meta_monitor_manager_get ();
meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL); meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL);
if (cursor_sprite)
rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
else
rect = (MetaRectangle) { 0 };
for (i = 0; i < n_crtcs; i++) for (i = 0; i < n_crtcs; i++)
{ {
gboolean crtc_should_have_cursor; gboolean crtc_should_use_cursor;
MetaCursorSprite *crtc_cursor; MetaCursorSprite *crtc_cursor;
MetaRectangle *crtc_rect; MetaRectangle *crtc_rect;
crtc_rect = &crtcs[i].rect; crtc_rect = &crtcs[i].rect;
crtc_should_have_cursor = (priv->has_hw_cursor && meta_rectangle_overlap (cursor_rect, crtc_rect)); crtc_should_use_cursor = (priv->has_hw_cursor &&
if (crtc_should_have_cursor) meta_rectangle_overlap (&rect, crtc_rect));
if (crtc_should_use_cursor)
crtc_cursor = cursor_sprite; crtc_cursor = cursor_sprite;
else else
crtc_cursor = NULL; crtc_cursor = NULL;
@ -162,49 +246,78 @@ update_hw_cursor (MetaCursorRendererNative *native,
if (crtc_cursor) if (crtc_cursor)
{ {
drmModeMoveCursor (priv->drm_fd, crtcs[i].crtc_id, drmModeMoveCursor (priv->drm_fd, crtcs[i].crtc_id,
cursor_rect->x - crtc_rect->x, rect.x - crtc_rect->x,
cursor_rect->y - crtc_rect->y); rect.y - crtc_rect->y);
} }
} }
} }
static gboolean static gboolean
should_have_hw_cursor (MetaCursorRenderer *renderer) has_valid_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
{ {
MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer); MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
if (cursor_sprite) switch (cursor_priv->pending_bo_state)
return (get_cursor_sprite_gbm_bo (cursor_sprite) != NULL); {
else case META_CURSOR_GBM_BO_STATE_NONE:
return get_active_cursor_sprite_gbm_bo (cursor_sprite) != NULL;
case META_CURSOR_GBM_BO_STATE_SET:
return TRUE;
case META_CURSOR_GBM_BO_STATE_INVALIDATED:
return FALSE;
}
g_assert_not_reached ();
return FALSE;
}
static gboolean
should_have_hw_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{
CoglTexture *texture;
if (!cursor_sprite)
return FALSE; return FALSE;
texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
if (!texture)
return FALSE;
if (meta_cursor_sprite_get_texture_scale (cursor_sprite) != 1)
return FALSE;
if (!has_valid_cursor_sprite_gbm_bo (cursor_sprite))
return FALSE;
return TRUE;
} }
static gboolean static gboolean
meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native) meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native)
{ {
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
MetaCursorSprite *cursor_sprite; MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
priv->animation_timeout_id = 0; priv->animation_timeout_id = 0;
cursor_sprite =
meta_cursor_renderer_get_cursor (META_CURSOR_RENDERER (native));
meta_cursor_sprite_tick_frame (cursor_sprite); meta_cursor_sprite_tick_frame (cursor_sprite);
meta_cursor_renderer_force_update (META_CURSOR_RENDERER (native)); meta_cursor_renderer_force_update (renderer);
meta_cursor_renderer_native_force_update (native); meta_cursor_renderer_native_force_update (native);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static void static void
meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native) meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native,
MetaCursorSprite *cursor_sprite)
{ {
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
MetaCursorSprite *cursor_sprite;
gboolean cursor_change; gboolean cursor_change;
guint delay; guint delay;
cursor_sprite =
meta_cursor_renderer_get_cursor (META_CURSOR_RENDERER (native));
cursor_change = cursor_sprite != priv->last_cursor; cursor_change = cursor_sprite != priv->last_cursor;
priv->last_cursor = cursor_sprite; priv->last_cursor = cursor_sprite;
@ -234,15 +347,19 @@ meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native)
} }
static gboolean static gboolean
meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer) meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
meta_cursor_renderer_native_trigger_frame (native); if (cursor_sprite)
meta_cursor_sprite_realize_texture (cursor_sprite);
priv->has_hw_cursor = should_have_hw_cursor (renderer); meta_cursor_renderer_native_trigger_frame (native, cursor_sprite);
update_hw_cursor (native, FALSE);
priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite);
update_hw_cursor (native, cursor_sprite, FALSE);
return priv->has_hw_cursor; return priv->has_hw_cursor;
} }
@ -257,6 +374,38 @@ get_hardware_cursor_size (MetaCursorRendererNative *native,
*height = priv->cursor_height; *height = priv->cursor_height;
} }
static void
cursor_priv_free (gpointer data)
{
MetaCursorNativePrivate *cursor_priv = data;
guint i;
if (!data)
return;
for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++)
g_clear_pointer (&cursor_priv->bos[0], (GDestroyNotify) gbm_bo_destroy);
g_slice_free (MetaCursorNativePrivate, cursor_priv);
}
static MetaCursorNativePrivate *
ensure_cursor_priv (MetaCursorSprite *cursor_sprite)
{
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
if (!cursor_priv)
{
cursor_priv = g_slice_new0 (MetaCursorNativePrivate);
g_object_set_qdata_full (G_OBJECT (cursor_sprite),
quark_cursor_sprite,
cursor_priv,
cursor_priv_free);
}
return cursor_priv;
}
static void static void
load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native, load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native,
MetaCursorSprite *cursor_sprite, MetaCursorSprite *cursor_sprite,
@ -288,17 +437,45 @@ load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native,
bo = gbm_bo_create (priv->gbm, cursor_width, cursor_height, bo = gbm_bo_create (priv->gbm, cursor_width, cursor_height,
gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!bo)
{
meta_warning ("Failed to allocate HW cursor buffer\n");
return;
}
memset (buf, 0, sizeof(buf)); memset (buf, 0, sizeof(buf));
for (i = 0; i < height; i++) for (i = 0; i < height; i++)
memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4); memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4);
if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0)
{
meta_warning ("Failed to write cursors buffer data: %s",
g_strerror (errno));
gbm_bo_destroy (bo);
return;
}
gbm_bo_write (bo, buf, cursor_width * cursor_height * 4); set_pending_cursor_sprite_gbm_bo (cursor_sprite, bo);
set_cursor_sprite_gbm_bo (cursor_sprite, bo);
} }
else else
meta_warning ("HW cursor for format %d not supported\n", gbm_format); {
meta_warning ("HW cursor for format %d not supported\n", gbm_format);
}
}
static void
invalidate_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
{
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
guint pending_bo;
if (!cursor_priv)
return;
pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
g_clear_pointer (&cursor_priv->bos[pending_bo],
(GDestroyNotify) gbm_bo_destroy);
cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED;
} }
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
@ -312,10 +489,17 @@ meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *r
meta_cursor_renderer_native_get_instance_private (native); meta_cursor_renderer_native_get_instance_private (native);
uint32_t gbm_format; uint32_t gbm_format;
uint64_t cursor_width, cursor_height; uint64_t cursor_width, cursor_height;
CoglTexture *texture;
uint width, height; uint width, height;
width = meta_cursor_sprite_get_width (cursor_sprite); /* Destroy any previous pending cursor buffer; we'll always either fail (which
height = meta_cursor_sprite_get_height (cursor_sprite); * should unset, or succeed, which will set new buffer.
*/
invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite);
texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
width = cogl_texture_get_width (texture);
height = cogl_texture_get_height (texture);
struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (buffer); struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (buffer);
if (shm_buffer) if (shm_buffer)
@ -384,7 +568,7 @@ meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *r
return; return;
} }
set_cursor_sprite_gbm_bo (cursor_sprite, bo); set_pending_cursor_sprite_gbm_bo (cursor_sprite, bo);
} }
} }
#endif #endif
@ -396,6 +580,8 @@ meta_cursor_renderer_native_realize_cursor_from_xcursor (MetaCursorRenderer *ren
{ {
MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite);
load_cursor_sprite_gbm_buffer (native, load_cursor_sprite_gbm_buffer (native,
cursor_sprite, cursor_sprite,
(uint8_t *) xc_image->pixels, (uint8_t *) xc_image->pixels,
@ -423,12 +609,20 @@ meta_cursor_renderer_native_class_init (MetaCursorRendererNativeClass *klass)
quark_cursor_sprite = g_quark_from_static_string ("-meta-cursor-native"); quark_cursor_sprite = g_quark_from_static_string ("-meta-cursor-native");
} }
static void
force_update_hw_cursor (MetaCursorRendererNative *native)
{
MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
update_hw_cursor (native, meta_cursor_renderer_get_cursor (renderer), TRUE);
}
static void static void
on_monitors_changed (MetaMonitorManager *monitors, on_monitors_changed (MetaMonitorManager *monitors,
MetaCursorRendererNative *native) MetaCursorRendererNative *native)
{ {
/* Our tracking is all messed up, so force an update. */ /* Our tracking is all messed up, so force an update. */
update_hw_cursor (native, TRUE); force_update_hw_cursor (native);
} }
static void static void
@ -476,5 +670,5 @@ meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *native)
void void
meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native) meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native)
{ {
update_hw_cursor (native, TRUE); force_update_hw_cursor (native);
} }

View File

@ -40,25 +40,29 @@ typedef struct _MetaCursorRendererX11Private MetaCursorRendererX11Private;
G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererX11, meta_cursor_renderer_x11, META_TYPE_CURSOR_RENDERER); G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererX11, meta_cursor_renderer_x11, META_TYPE_CURSOR_RENDERER);
static gboolean static gboolean
meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer) meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
MetaCursorRendererX11 *x11 = META_CURSOR_RENDERER_X11 (renderer); MetaCursorRendererX11 *x11 = META_CURSOR_RENDERER_X11 (renderer);
MetaCursorRendererX11Private *priv = meta_cursor_renderer_x11_get_instance_private (x11); MetaCursorRendererX11Private *priv = meta_cursor_renderer_x11_get_instance_private (x11);
MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ());
Window xwindow = meta_backend_x11_get_xwindow (backend); Window xwindow = meta_backend_x11_get_xwindow (backend);
if (xwindow == None)
return FALSE;
Display *xdisplay = meta_backend_x11_get_xdisplay (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend);
MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer); if (xwindow == None)
{
if (cursor_sprite)
meta_cursor_sprite_realize_texture (cursor_sprite);
return FALSE;
}
gboolean has_server_cursor = FALSE; gboolean has_server_cursor = FALSE;
if (cursor_sprite) if (cursor_sprite)
{ {
MetaCursor cursor = meta_cursor_sprite_get_meta_cursor (cursor_sprite); MetaCursor cursor = meta_cursor_sprite_get_meta_cursor (cursor_sprite);
if (cursor != META_CURSOR_NONE) if (cursor != META_CURSOR_NONE)
{ {
Cursor xcursor = meta_cursor_create_x_cursor (xdisplay, cursor); Cursor xcursor = meta_cursor_create_x_cursor (xdisplay, cursor);
@ -80,6 +84,9 @@ meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer)
priv->server_cursor_visible = has_server_cursor; priv->server_cursor_visible = has_server_cursor;
} }
if (!priv->server_cursor_visible && cursor_sprite)
meta_cursor_sprite_realize_texture (cursor_sprite);
return priv->server_cursor_visible; return priv->server_cursor_visible;
} }

View File

@ -39,8 +39,11 @@ G_DEFINE_TYPE (MetaCursorRendererX11Nested, meta_cursor_renderer_x11_nested,
META_TYPE_CURSOR_RENDERER); META_TYPE_CURSOR_RENDERER);
static gboolean static gboolean
meta_cursor_renderer_x11_nested_update_cursor (MetaCursorRenderer *renderer) meta_cursor_renderer_x11_nested_update_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{ {
if (cursor_sprite)
meta_cursor_sprite_realize_texture (cursor_sprite);
return FALSE; return FALSE;
} }

View File

@ -152,6 +152,10 @@ const MetaMonitorInfo* meta_screen_get_monitor_for_rect (MetaScreen *screen
const MetaMonitorInfo* meta_screen_calculate_monitor_for_window (MetaScreen *screen, const MetaMonitorInfo* meta_screen_calculate_monitor_for_window (MetaScreen *screen,
MetaWindow *window); MetaWindow *window);
const MetaMonitorInfo* meta_screen_get_monitor_for_point (MetaScreen *screen,
int x,
int y);
const MetaMonitorInfo* meta_screen_get_monitor_neighbor (MetaScreen *screen, const MetaMonitorInfo* meta_screen_get_monitor_neighbor (MetaScreen *screen,
int which_monitor, int which_monitor,

View File

@ -43,6 +43,7 @@
#include <meta/meta-enum-types.h> #include <meta/meta-enum-types.h>
#include "core.h" #include "core.h"
#include "meta-cursor-tracker-private.h" #include "meta-cursor-tracker-private.h"
#include "boxes-private.h"
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
#include <X11/extensions/Xcomposite.h> #include <X11/extensions/Xcomposite.h>
@ -1255,6 +1256,31 @@ update_num_workspaces (MetaScreen *screen,
g_object_notify (G_OBJECT (screen), "n-workspaces"); g_object_notify (G_OBJECT (screen), "n-workspaces");
} }
static void
root_cursor_prepare_at (MetaCursorSprite *cursor_sprite,
int x,
int y,
MetaScreen *screen)
{
const MetaMonitorInfo *monitor;
monitor = meta_screen_get_monitor_for_point (screen, x, y);
/* Reload the cursor texture if the scale has changed. */
meta_cursor_sprite_set_theme_scale (cursor_sprite, monitor->scale);
}
static void
manage_root_cursor_sprite_scale (MetaScreen *screen,
MetaCursorSprite *cursor_sprite)
{
g_signal_connect_object (cursor_sprite,
"prepare-at",
G_CALLBACK (root_cursor_prepare_at),
screen,
0);
}
void void
meta_screen_update_cursor (MetaScreen *screen) meta_screen_update_cursor (MetaScreen *screen)
{ {
@ -1265,6 +1291,10 @@ meta_screen_update_cursor (MetaScreen *screen)
MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (screen); MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (screen);
cursor_sprite = meta_cursor_sprite_from_theme (cursor); cursor_sprite = meta_cursor_sprite_from_theme (cursor);
if (meta_is_wayland_compositor ())
manage_root_cursor_sprite_scale (screen, cursor_sprite);
meta_cursor_tracker_set_root_cursor (tracker, cursor_sprite); meta_cursor_tracker_set_root_cursor (tracker, cursor_sprite);
g_object_unref (cursor_sprite); g_object_unref (cursor_sprite);
@ -1454,6 +1484,25 @@ meta_screen_get_monitor_index_for_rect (MetaScreen *screen,
return monitor->number; return monitor->number;
} }
const MetaMonitorInfo *
meta_screen_get_monitor_for_point (MetaScreen *screen,
int x,
int y)
{
int i;
if (screen->n_monitor_infos == 1)
return &screen->monitor_infos[0];
for (i = 0; i < screen->n_monitor_infos; i++)
{
if (POINT_IN_RECT (x, y, screen->monitor_infos[i].rect))
return &screen->monitor_infos[i];
}
return NULL;
}
const MetaMonitorInfo* const MetaMonitorInfo*
meta_screen_get_monitor_neighbor (MetaScreen *screen, meta_screen_get_monitor_neighbor (MetaScreen *screen,
int which_monitor, int which_monitor,

View File

@ -44,15 +44,22 @@
#include "config.h" #include "config.h"
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <cogl/cogl-wayland-server.h>
#include <linux/input.h> #include <linux/input.h>
#include "meta-wayland-pointer.h" #include "meta-wayland-pointer.h"
#include "meta-wayland-popup.h" #include "meta-wayland-popup.h"
#include "meta-wayland-private.h" #include "meta-wayland-private.h"
#include "meta-wayland-surface.h"
#include "meta-wayland-buffer.h" #include "meta-wayland-buffer.h"
#include "meta-cursor.h" #include "meta-cursor.h"
#include "meta-cursor-tracker-private.h" #include "meta-cursor-tracker-private.h"
#include "meta-surface-actor-wayland.h" #include "meta-surface-actor-wayland.h"
#include "meta/meta-cursor-tracker.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-cursor-renderer.h"
#include <string.h> #include <string.h>
@ -61,6 +68,10 @@
struct _MetaWaylandSurfaceRoleCursor struct _MetaWaylandSurfaceRoleCursor
{ {
MetaWaylandSurfaceRole parent; MetaWaylandSurfaceRole parent;
int hot_x;
int hot_y;
MetaCursorSprite *cursor_sprite;
}; };
GType meta_wayland_surface_role_cursor_get_type (void) G_GNUC_CONST; GType meta_wayland_surface_role_cursor_get_type (void) G_GNUC_CONST;
@ -215,32 +226,6 @@ sync_focus_surface (MetaWaylandPointer *pointer)
} }
static void
set_cursor_surface (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface)
{
if (pointer->cursor_surface == surface)
return;
if (pointer->cursor_surface)
wl_list_remove (&pointer->cursor_surface_destroy_listener.link);
pointer->cursor_surface = surface;
if (pointer->cursor_surface)
wl_resource_add_destroy_listener (pointer->cursor_surface->resource,
&pointer->cursor_surface_destroy_listener);
}
static void
pointer_handle_cursor_surface_destroy (struct wl_listener *listener, void *data)
{
MetaWaylandPointer *pointer = wl_container_of (listener, pointer, cursor_surface_destroy_listener);
set_cursor_surface (pointer, NULL);
meta_wayland_pointer_update_cursor_surface (pointer);
}
static void static void
pointer_handle_focus_surface_destroy (struct wl_listener *listener, void *data) pointer_handle_focus_surface_destroy (struct wl_listener *listener, void *data)
{ {
@ -376,7 +361,6 @@ meta_wayland_pointer_init (MetaWaylandPointer *pointer,
pointer->focus_surface_listener.notify = pointer_handle_focus_surface_destroy; pointer->focus_surface_listener.notify = pointer_handle_focus_surface_destroy;
pointer->cursor_surface = NULL; pointer->cursor_surface = NULL;
pointer->cursor_surface_destroy_listener.notify = pointer_handle_cursor_surface_destroy;
pointer->default_grab.interface = &default_pointer_grab_interface; pointer->default_grab.interface = &default_pointer_grab_interface;
pointer->default_grab.pointer = pointer; pointer->default_grab.pointer = pointer;
@ -390,10 +374,10 @@ void
meta_wayland_pointer_release (MetaWaylandPointer *pointer) meta_wayland_pointer_release (MetaWaylandPointer *pointer)
{ {
meta_wayland_pointer_set_focus (pointer, NULL); meta_wayland_pointer_set_focus (pointer, NULL);
set_cursor_surface (pointer, NULL);
g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref); g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref);
pointer->display = NULL; pointer->display = NULL;
pointer->cursor_surface = NULL;
} }
static int static int
@ -742,22 +726,17 @@ meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer)
if (pointer->current) if (pointer->current)
{ {
MetaCursorSprite *cursor_sprite; MetaCursorSprite *cursor_sprite = NULL;
if (pointer->cursor_surface && pointer->cursor_surface->buffer) if (pointer->cursor_surface)
{ {
struct wl_resource *buffer = pointer->cursor_surface->buffer->resource; MetaWaylandSurfaceRoleCursor *cursor_role =
cursor_sprite = meta_cursor_sprite_from_buffer (buffer, META_WAYLAND_SURFACE_ROLE_CURSOR (pointer->cursor_surface->role);
pointer->hotspot_x,
pointer->hotspot_y); cursor_sprite = cursor_role->cursor_sprite;
} }
else
cursor_sprite = NULL;
meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite); meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite);
if (cursor_sprite)
g_object_unref (cursor_sprite);
} }
else else
{ {
@ -765,12 +744,76 @@ meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer)
} }
} }
static void
update_cursor_sprite_texture (MetaWaylandSurface *surface)
{
MetaCursorRenderer *cursor_renderer =
meta_backend_get_cursor_renderer (meta_get_backend ());
MetaWaylandSurfaceRoleCursor *cursor_role =
META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
MetaCursorSprite *cursor_sprite = cursor_role->cursor_sprite;
ClutterBackend *clutter_backend = clutter_get_default_backend ();
CoglContext *cogl_context =
clutter_backend_get_cogl_context (clutter_backend);
CoglTexture *texture;
if (surface->buffer)
{
struct wl_resource *buffer;
buffer = surface->buffer->resource;
texture = cogl_wayland_texture_2d_new_from_buffer (cogl_context,
buffer,
NULL);
meta_cursor_sprite_set_texture (cursor_sprite,
texture,
cursor_role->hot_x * surface->scale,
cursor_role->hot_y * surface->scale);
meta_cursor_renderer_realize_cursor_from_wl_buffer (cursor_renderer,
cursor_sprite,
buffer);
cogl_object_unref (texture);
}
else
{
meta_cursor_sprite_set_texture (cursor_sprite, NULL, 0, 0);
}
meta_cursor_renderer_force_update (cursor_renderer);
}
static void
cursor_sprite_prepare_at (MetaCursorSprite *cursor_sprite,
int x,
int y,
MetaWaylandSurfaceRoleCursor *cursor_role)
{
MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_role);
MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role);
MetaDisplay *display = meta_get_display ();
MetaScreen *screen = display->screen;
const MetaMonitorInfo *monitor;
monitor = meta_screen_get_monitor_for_point (screen, x, y);
meta_cursor_sprite_set_texture_scale (cursor_sprite,
(float)monitor->scale / surface->scale);
}
static void
meta_wayland_pointer_set_cursor_surface (MetaWaylandPointer *pointer,
MetaWaylandSurface *cursor_surface)
{
pointer->cursor_surface = cursor_surface;
meta_wayland_pointer_update_cursor_surface (pointer);
}
static void static void
pointer_set_cursor (struct wl_client *client, pointer_set_cursor (struct wl_client *client,
struct wl_resource *resource, struct wl_resource *resource,
uint32_t serial, uint32_t serial,
struct wl_resource *surface_resource, struct wl_resource *surface_resource,
int32_t x, int32_t y) int32_t hot_x, int32_t hot_y)
{ {
MetaWaylandPointer *pointer = wl_resource_get_user_data (resource); MetaWaylandPointer *pointer = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface; MetaWaylandSurface *surface;
@ -794,10 +837,28 @@ pointer_set_cursor (struct wl_client *client,
return; return;
} }
pointer->hotspot_x = x; if (surface)
pointer->hotspot_y = y; {
set_cursor_surface (pointer, surface); MetaWaylandSurfaceRoleCursor *cursor_role;
meta_wayland_pointer_update_cursor_surface (pointer);
cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
if (!cursor_role->cursor_sprite)
{
cursor_role->cursor_sprite = meta_cursor_sprite_new ();
g_signal_connect_object (cursor_role->cursor_sprite,
"prepare-at",
G_CALLBACK (cursor_sprite_prepare_at),
cursor_role,
0);
}
cursor_role->hot_x = hot_x;
cursor_role->hot_y = hot_y;
update_cursor_sprite_texture (surface);
}
meta_wayland_pointer_set_cursor_surface (pointer, surface);
} }
static void static void
@ -877,12 +938,35 @@ cursor_surface_role_commit (MetaWaylandSurfaceRole *surface_role,
{ {
MetaWaylandSurface *surface = MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role); meta_wayland_surface_role_get_surface (surface_role);
MetaWaylandPointer *pointer = &surface->compositor->seat->pointer;
meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending); meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
if (pending->newly_attached) if (pending->newly_attached)
meta_wayland_pointer_update_cursor_surface (pointer); update_cursor_sprite_texture (surface);
}
static void
cursor_surface_role_dispose (GObject *object)
{
MetaWaylandSurfaceRoleCursor *cursor_role =
META_WAYLAND_SURFACE_ROLE_CURSOR (object);
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object));
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandPointer *pointer = &compositor->seat->pointer;
MetaCursorTracker *cursor_tracker = meta_cursor_tracker_get_for_screen (NULL);
g_signal_handlers_disconnect_by_func (cursor_tracker,
(gpointer) cursor_sprite_prepare_at,
cursor_role);
if (pointer->cursor_surface == surface)
pointer->cursor_surface = NULL;
meta_wayland_pointer_update_cursor_surface (pointer);
g_clear_object (&cursor_role->cursor_sprite);
G_OBJECT_CLASS (meta_wayland_surface_role_cursor_parent_class)->dispose (object);
} }
static void static void
@ -895,7 +979,10 @@ meta_wayland_surface_role_cursor_class_init (MetaWaylandSurfaceRoleCursorClass *
{ {
MetaWaylandSurfaceRoleClass *surface_role_class = MetaWaylandSurfaceRoleClass *surface_role_class =
META_WAYLAND_SURFACE_ROLE_CLASS (klass); META_WAYLAND_SURFACE_ROLE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
surface_role_class->assigned = cursor_surface_role_assigned; surface_role_class->assigned = cursor_surface_role_assigned;
surface_role_class->commit = cursor_surface_role_commit; surface_role_class->commit = cursor_surface_role_commit;
object_class->dispose = cursor_surface_role_dispose;
} }

View File

@ -73,8 +73,6 @@ struct _MetaWaylandPointer
guint32 click_serial; guint32 click_serial;
MetaWaylandSurface *cursor_surface; MetaWaylandSurface *cursor_surface;
struct wl_listener cursor_surface_destroy_listener;
int hotspot_x, hotspot_y;
MetaWaylandPointerGrab *grab; MetaWaylandPointerGrab *grab;
MetaWaylandPointerGrab default_grab; MetaWaylandPointerGrab default_grab;