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:
@ -35,9 +35,13 @@
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
#include <cogl/cogl-wayland-server.h>
|
||||
#endif
|
||||
enum {
|
||||
PREPARE_AT,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
struct _MetaCursorSprite
|
||||
{
|
||||
@ -46,10 +50,14 @@ struct _MetaCursorSprite
|
||||
MetaCursor cursor;
|
||||
|
||||
CoglTexture2D *texture;
|
||||
float texture_scale;
|
||||
int hot_x, hot_y;
|
||||
|
||||
int current_frame;
|
||||
XcursorImages *xcursor_images;
|
||||
|
||||
int theme_scale;
|
||||
gboolean theme_dirty;
|
||||
};
|
||||
|
||||
GType meta_cursor_sprite_get_type (void) G_GNUC_CONST;
|
||||
@ -112,11 +120,11 @@ meta_cursor_create_x_cursor (Display *xdisplay,
|
||||
}
|
||||
|
||||
static XcursorImages *
|
||||
load_cursor_on_client (MetaCursor cursor)
|
||||
load_cursor_on_client (MetaCursor cursor, int scale)
|
||||
{
|
||||
return XcursorLibraryLoadImages (translate_meta_cursor (cursor),
|
||||
meta_prefs_get_cursor_theme (),
|
||||
meta_prefs_get_cursor_size ());
|
||||
meta_prefs_get_cursor_size () * scale);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -129,6 +137,7 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
|
||||
CoglPixelFormat cogl_format;
|
||||
ClutterBackend *clutter_backend;
|
||||
CoglContext *cogl_context;
|
||||
CoglTexture *texture;
|
||||
|
||||
g_assert (self->texture == NULL);
|
||||
|
||||
@ -144,14 +153,15 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
|
||||
|
||||
clutter_backend = clutter_get_default_backend ();
|
||||
cogl_context = clutter_backend_get_cogl_context (clutter_backend);
|
||||
self->texture = cogl_texture_2d_new_from_data (cogl_context,
|
||||
width, height,
|
||||
cogl_format,
|
||||
rowstride,
|
||||
(uint8_t *) xc_image->pixels,
|
||||
NULL);
|
||||
self->hot_x = xc_image->xhot;
|
||||
self->hot_y = xc_image->yhot;
|
||||
texture = cogl_texture_2d_new_from_data (cogl_context,
|
||||
width, height,
|
||||
cogl_format,
|
||||
rowstride,
|
||||
(uint8_t *) xc_image->pixels,
|
||||
NULL);
|
||||
meta_cursor_sprite_set_texture (self, texture,
|
||||
xc_image->xhot, xc_image->yhot);
|
||||
cogl_object_unref (texture);
|
||||
|
||||
meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image);
|
||||
}
|
||||
@ -198,92 +208,82 @@ meta_cursor_sprite_is_animated (MetaCursorSprite *self)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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->xcursor_images = load_cursor_on_client (self->cursor);
|
||||
self->xcursor_images = load_cursor_on_client (self->cursor,
|
||||
self->theme_scale);
|
||||
if (!self->xcursor_images)
|
||||
meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?");
|
||||
|
||||
image = meta_cursor_sprite_get_current_frame_image (self);
|
||||
meta_cursor_sprite_load_from_xcursor_image (self, image);
|
||||
|
||||
return self;
|
||||
self->theme_dirty = FALSE;
|
||||
}
|
||||
|
||||
MetaCursorSprite *
|
||||
meta_cursor_sprite_from_texture (CoglTexture2D *texture,
|
||||
int hot_x,
|
||||
int hot_y)
|
||||
meta_cursor_sprite_from_theme (MetaCursor cursor)
|
||||
{
|
||||
MetaCursorSprite *self;
|
||||
|
||||
self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
|
||||
self = meta_cursor_sprite_new ();
|
||||
|
||||
cogl_object_ref (texture);
|
||||
|
||||
self->texture = texture;
|
||||
self->hot_x = hot_x;
|
||||
self->hot_y = hot_y;
|
||||
self->cursor = cursor;
|
||||
self->theme_dirty = TRUE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
static void
|
||||
meta_cursor_sprite_load_from_buffer (MetaCursorSprite *self,
|
||||
struct wl_resource *buffer,
|
||||
int hot_x,
|
||||
int hot_y)
|
||||
void
|
||||
meta_cursor_sprite_set_texture (MetaCursorSprite *self,
|
||||
CoglTexture *texture,
|
||||
int hot_x,
|
||||
int hot_y)
|
||||
{
|
||||
MetaBackend *meta_backend = meta_get_backend ();
|
||||
MetaCursorRenderer *renderer =
|
||||
meta_backend_get_cursor_renderer (meta_backend);
|
||||
ClutterBackend *backend;
|
||||
CoglContext *cogl_context;
|
||||
|
||||
g_clear_pointer (&self->texture, cogl_object_unref);
|
||||
if (texture)
|
||||
self->texture = cogl_object_ref (texture);
|
||||
self->hot_x = hot_x;
|
||||
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 *
|
||||
meta_cursor_sprite_from_buffer (struct wl_resource *buffer,
|
||||
int hot_x,
|
||||
int hot_y)
|
||||
void
|
||||
meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
|
||||
float scale)
|
||||
{
|
||||
MetaCursorSprite *self;
|
||||
|
||||
self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
|
||||
|
||||
meta_cursor_sprite_load_from_buffer (self, buffer, hot_x, hot_y);
|
||||
|
||||
return self;
|
||||
self->texture_scale = scale;
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
|
||||
int theme_scale)
|
||||
{
|
||||
if (self->theme_scale != theme_scale)
|
||||
self->theme_dirty = TRUE;
|
||||
self->theme_scale = theme_scale;
|
||||
}
|
||||
#endif
|
||||
|
||||
CoglTexture *
|
||||
meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self,
|
||||
int *hot_x,
|
||||
int *hot_y)
|
||||
meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self)
|
||||
{
|
||||
if (hot_x)
|
||||
*hot_x = self->hot_x;
|
||||
if (hot_y)
|
||||
*hot_y = self->hot_y;
|
||||
|
||||
return COGL_TEXTURE (self->texture);
|
||||
}
|
||||
|
||||
@ -302,21 +302,31 @@ meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
|
||||
*hot_y = self->hot_y;
|
||||
}
|
||||
|
||||
guint
|
||||
meta_cursor_sprite_get_width (MetaCursorSprite *self)
|
||||
float
|
||||
meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self)
|
||||
{
|
||||
return cogl_texture_get_width (COGL_TEXTURE (self->texture));
|
||||
return self->texture_scale;
|
||||
}
|
||||
|
||||
guint
|
||||
meta_cursor_sprite_get_height (MetaCursorSprite *self)
|
||||
void
|
||||
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
|
||||
meta_cursor_sprite_init (MetaCursorSprite *self)
|
||||
{
|
||||
self->texture_scale = 1.0f;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -338,4 +348,13 @@ meta_cursor_sprite_class_init (MetaCursorSpriteClass *klass)
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user