Wayland backend: cursor icon functions

This commit is contained in:
Riccardo Canalicchio 2021-12-18 16:17:30 +01:00
parent 5db58c071d
commit 54b2b172d8
6 changed files with 245 additions and 42 deletions

View file

@ -30,6 +30,21 @@
#include "cairo/CairoSurface.h" #include "cairo/CairoSurface.h"
struct pool_buffer
{
int poolfd;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
cairo_surface_t *surface;
uint32_t width, height;
void *data;
size_t size;
bool busy;
};
struct pool_buffer *
createShmBuffer(int width, int height, struct wl_shm *shm);
@interface WaylandCairoShmSurface : CairoSurface @interface WaylandCairoShmSurface : CairoSurface
{ {
} }

View file

@ -34,6 +34,7 @@
#include <GNUstepGUI/GSDisplayServer.h> #include <GNUstepGUI/GSDisplayServer.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <wayland-cursor.h>
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
@ -60,6 +61,15 @@ struct pointer
uint32_t serial; uint32_t serial;
struct window *focus; struct window *focus;
};
struct cursor
{
struct wl_cursor *cursor;
struct wl_surface *surface;
struct wl_cursor_image *image;
struct wl_buffer *buffer;
}; };
typedef struct _WaylandConfig typedef struct _WaylandConfig
@ -73,6 +83,7 @@ typedef struct _WaylandConfig
struct wl_keyboard *keyboard; struct wl_keyboard *keyboard;
struct xdg_wm_base *wm_base; struct xdg_wm_base *wm_base;
struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_shell_v1 *layer_shell;
int seat_version;
struct wl_list output_list; struct wl_list output_list;
int output_count; int output_count;
@ -80,7 +91,20 @@ typedef struct _WaylandConfig
int window_count; int window_count;
int last_window_id; int last_window_id;
// last event serial from pointer or keyboard
uint32_t event_serial;
// cursor
struct wl_cursor_theme *cursor_theme;
struct cursor *cursor;
struct wl_surface *cursor_surface;
// pointer
struct pointer pointer; struct pointer pointer;
float mouse_scroll_multiplier;
// keyboard
struct xkb_context *xkb_context; struct xkb_context *xkb_context;
struct struct
{ {
@ -92,9 +116,6 @@ typedef struct _WaylandConfig
} xkb; } xkb;
int modifiers; int modifiers;
int seat_version;
float mouse_scroll_multiplier;
} WaylandConfig; } WaylandConfig;
struct output struct output
@ -147,6 +168,8 @@ struct window
CairoSurface *wcs; CairoSurface *wcs;
}; };
struct window *get_window_with_id(WaylandConfig *wlconfig, int winid);
@interface WaylandServer : GSDisplayServer @interface WaylandServer : GSDisplayServer
{ {
WaylandConfig *wlconfig; WaylandConfig *wlconfig;

View file

@ -44,17 +44,6 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
struct pool_buffer
{
int poolfd;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
cairo_surface_t *surface;
uint32_t width, height;
void *data;
size_t size;
bool busy;
};
static const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888; static const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888;
static const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32; static const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32;
@ -135,11 +124,11 @@ createPoolFile(off_t size)
return fd; return fd;
} }
static struct pool_buffer * struct pool_buffer *
createBufferForWindow(struct window * window, struct wl_shm *shm) createShmBuffer(int width, int height, struct wl_shm *shm)
{ {
uint32_t stride = cairo_format_stride_for_width(cairo_fmt, window->width); uint32_t stride = cairo_format_stride_for_width(cairo_fmt, width);
size_t size = stride * window->height; size_t size = stride * height;
struct pool_buffer * buf = malloc(sizeof(struct pool_buffer)); struct pool_buffer * buf = malloc(sizeof(struct pool_buffer));
@ -160,22 +149,28 @@ createBufferForWindow(struct window * window, struct wl_shm *shm)
} }
buf->pool = wl_shm_create_pool(shm, buf->poolfd, size); buf->pool = wl_shm_create_pool(shm, buf->poolfd, size);
buf->buffer = wl_shm_pool_create_buffer(buf->pool, 0, window->width, window->height, buf->buffer = wl_shm_pool_create_buffer(buf->pool, 0, width, height,
stride, wl_fmt); stride, wl_fmt);
wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
} }
else
{
return NULL;
}
buf->data = data; buf->data = data;
buf->size = size; buf->size = size;
buf->width = window->width; buf->width = width;
buf->height = window->height; buf->height = height;
buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, window->width, window->height, stride); buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, width, height, stride);
if(buf->pool)
{
wl_shm_pool_destroy(buf->pool); wl_shm_pool_destroy(buf->pool);
}
return buf; return buf;
} }
@implementation WaylandCairoShmSurface @implementation WaylandCairoShmSurface
{ {
struct pool_buffer *pbuffer; struct pool_buffer *pbuffer;
@ -188,7 +183,7 @@ createBufferForWindow(struct window * window, struct wl_shm *shm)
gsDevice = device; gsDevice = device;
pbuffer = createBufferForWindow(window, window->wlconfig->shm); pbuffer = createShmBuffer(window->width, window->height, window->wlconfig->shm);
if (pbuffer == NULL) if (pbuffer == NULL)
{ {

View file

@ -29,9 +29,13 @@
#import <AppKit/NSEvent.h> #import <AppKit/NSEvent.h>
#import <AppKit/NSView.h> #import <AppKit/NSView.h>
#import <AppKit/NSWindow.h> #import <AppKit/NSWindow.h>
#import <AppKit/NSCursor.h>
#import <AppKit/NSGraphics.h>
#import <AppKit/NSBitmapImageRep.h>
#import <GNUstepGUI/GSWindowDecorationView.h> #import <GNUstepGUI/GSWindowDecorationView.h>
#import <GNUstepGUI/GSTheme.h> #import <GNUstepGUI/GSTheme.h>
#include <linux/input.h> #include <linux/input.h>
#include "wayland-cursor.h"
// XXX should this be configurable by the user? // XXX should this be configurable by the user?
#define DOUBLECLICK_DELAY 300 #define DOUBLECLICK_DELAY 300
@ -53,11 +57,13 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
{ {
return; return;
} }
[(WaylandServer *)GSCurrentServer() initializeMouseIfRequired];
NSDebugLog(@"[%d] pointer_handle_enter",window->window_id); NSDebugLog(@"[%d] pointer_handle_enter",window->window_id);
float sx = wl_fixed_to_double(sx_w); float sx = wl_fixed_to_double(sx_w);
float sy = wl_fixed_to_double(sy_w); float sy = wl_fixed_to_double(sy_w);
[(WaylandServer *)GSCurrentServer() initializeMouseIfRequired];
wlconfig->pointer.focus = window; wlconfig->pointer.focus = window;
@ -93,6 +99,7 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
wlconfig->pointer.x = sx; wlconfig->pointer.x = sx;
wlconfig->pointer.y = sy; wlconfig->pointer.y = sy;
wlconfig->pointer.serial = serial;
} }
static void static void
@ -141,7 +148,7 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial,
[GSCurrentServer() postEvent:event atStart:NO]; [GSCurrentServer() postEvent:event atStart:NO];
wlconfig->pointer.focus = NULL; wlconfig->pointer.focus = NULL;
wlconfig->pointer.serial = 0; wlconfig->pointer.serial = serial;
} }
} }
@ -152,7 +159,7 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time,
{ {
WaylandConfig *wlconfig = data; WaylandConfig *wlconfig = data;
struct window *focused_window = wlconfig->pointer.focus; struct window *focused_window = wlconfig->pointer.focus;
if(window->ignoreMouse) if(focused_window->ignoreMouse)
{ {
return; return;
} }
@ -161,6 +168,7 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time,
wlconfig->pointer.last_timestamp = (NSTimeInterval) time / 1000.0; wlconfig->pointer.last_timestamp = (NSTimeInterval) time / 1000.0;
[(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired];
if (focused_window && wlconfig->pointer.serial) if (focused_window && wlconfig->pointer.serial)
@ -395,6 +403,7 @@ pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
wlconfig->pointer.button = button; wlconfig->pointer.button = button;
wlconfig->pointer.button_state = state; wlconfig->pointer.button_state = state;
wlconfig->pointer.last_timestamp = timestamp; wlconfig->pointer.last_timestamp = timestamp;
wlconfig->pointer.serial = serial;
} }
@ -586,22 +595,132 @@ WaylandServer (Cursor)
- (void)hidecursor - (void)hidecursor
{ {
NSDebugLog(@"hidecursor"); // to hide the cursor we set a NULL surface
wl_pointer_set_cursor(wlconfig->pointer.wlpointer, wlconfig->pointer.serial,
NULL, 0, 0);
} }
- (void)showcursor - (void)showcursor
{ {
NSDebugLog(@"showcursor"); // restore the previous surface
wl_pointer_set_cursor(wlconfig->pointer.wlpointer, wlconfig->pointer.serial,
wlconfig->cursor->surface,
wlconfig->cursor->image->hotspot_x,
wlconfig->cursor->image->hotspot_y);
} }
- (void)standardcursor:(int)style :(void **)cid - (void)standardcursor:(int)style :(void **)cid
{ {
NSDebugLog(@"standardcursor");
[self initializeMouseIfRequired];
char * cursor_name = "";
switch (style)
{
case GSArrowCursor:
cursor_name = "left_ptr";
break;
case GSIBeamCursor:
cursor_name = "xterm";
break;
case GSDragLinkCursor:
cursor_name = "dnd-link";
break;
case GSOperationNotAllowedCursor:
cursor_name = "X_cursor";
break;
case GSDragCopyCursor:
cursor_name = "dnd-copy";
break;
case GSPointingHandCursor:
cursor_name = "hand";
break;
case GSResizeLeftCursor:
cursor_name = "left_side";
break;
case GSResizeRightCursor:
cursor_name = "right_side";
break;
case GSResizeLeftRightCursor:
cursor_name = "sb_h_double_arrow";
break;
case GSCrosshairCursor:
cursor_name = "crosshair";
break;
case GSResizeUpCursor:
cursor_name = "top_side";
break;
case GSResizeDownCursor:
cursor_name = "bottom_side";
break;
case GSResizeUpDownCursor:
cursor_name = "sb_v_double_arrow";
break;
case GSDisappearingItemCursor:
cursor_name = "pirate";
break;
case GSContextualMenuCursor:
break;
case GSGreenArrowCursor:
break;
case GSClosedHandCursor:
break;
case GSOpenHandCursor:
break;
}
if (strlen(cursor_name) != 0)
{
NSDebugLog(@"load cursor from theme for style %d: %s", style,
cursor_name);
struct cursor *wayland_cursor = malloc(sizeof(struct cursor));
wayland_cursor->cursor
= wl_cursor_theme_get_cursor(wlconfig->cursor_theme, cursor_name);
wayland_cursor->image = wayland_cursor->cursor->images[0];
wayland_cursor->buffer
= wl_cursor_image_get_buffer(wayland_cursor->image);
*cid = wayland_cursor;
}
else
{
NSDebugLog(@"unable to load cursor from theme for style %d", style);
}
} }
- (void)imagecursor:(NSPoint)hotp :(NSImage *)image :(void **)cid - (void)imagecursor:(NSPoint)hotp :(NSImage *)image :(void **)cid
{ {
NSDebugLog(@"imagecursor"); NSBitmapImageRep* raw_img = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
unsigned char *data = [raw_img bitmapData];
NSSize imageSize = NSMakeSize(raw_img.pixelsWide, raw_img.pixelsHigh);
int width = imageSize.width;
int height = imageSize.height;
struct pool_buffer *pbuffer
= createShmBuffer(width, height, wlconfig->shm);
// TODO should check if the bitmaprep format is compatible
memcpy(pbuffer->data, data, [raw_img bytesPerPlane]);
struct cursor * wayland_cursor = malloc(sizeof(struct cursor));
struct wl_cursor * cursor = malloc(sizeof(struct wl_cursor));
cursor->image_count = 1;
cursor->name = "custom";
struct wl_cursor_image * cursor_image = malloc(sizeof(struct wl_cursor_image));
cursor->images = malloc(sizeof *cursor->images);
cursor->images[0] = cursor_image;
cursor_image->width = width;
cursor_image->height = height;
cursor_image->hotspot_x = hotp.x;
cursor_image->hotspot_y = hotp.y;
wayland_cursor->cursor = cursor;
wayland_cursor->image = cursor_image;
wayland_cursor->buffer = pbuffer->buffer;
*cid = wayland_cursor;
} }
- (void)setcursorcolor:(NSColor *)fg :(NSColor *)bg :(void *)cid - (void)setcursorcolor:(NSColor *)fg :(NSColor *)bg :(void *)cid
@ -613,18 +732,57 @@ WaylandServer (Cursor)
- (void) recolorcursor:(NSColor *)fg :(NSColor *)bg :(void*) cid - (void) recolorcursor:(NSColor *)fg :(NSColor *)bg :(void*) cid
{ {
// TODO recolorcursor
NSDebugLog(@"recolorcursor"); NSDebugLog(@"recolorcursor");
} }
- (void)setcursor:(void *)cid - (void)setcursor:(void *)cid
{ {
NSDebugLog(@"setcursor"); struct cursor *wayland_cursor = cid;
if(wayland_cursor == NULL)
{
return;
}
if(wayland_cursor->cursor == NULL)
{
return;
}
if(wayland_cursor->image == NULL)
{
return;
}
if(wayland_cursor->buffer == NULL)
{
return;
}
if(wayland_cursor->surface)
{
wl_surface_destroy(wayland_cursor->surface);
}
wl_pointer_set_cursor(wlconfig->pointer.wlpointer, wlconfig->event_serial,
wlconfig->cursor_surface,
wayland_cursor->image->hotspot_x,
wayland_cursor->image->hotspot_y);
wl_surface_attach(wlconfig->cursor_surface, wayland_cursor->buffer, 0, 0);
wl_surface_damage(wlconfig->cursor_surface, 0, 0,
wayland_cursor->image->width, wayland_cursor->image->height);
wl_surface_commit(wlconfig->cursor_surface);
wlconfig->cursor = wayland_cursor;
} }
- (void)freecursor:(void *)cid - (void)freecursor:(void *)cid
{ {
NSDebugLog(@"freecursor"); // the cursor should be deallocated
struct cursor * c = cid;
wl_cursor_destroy(c->cursor);
wl_buffer_destroy(c->buffer);
free(cid);
} }
- (void)setIgnoreMouse:(BOOL)ignoreMouse :(int)win - (void)setIgnoreMouse:(BOOL)ignoreMouse :(int)win
{ {
struct window *window = get_window_with_id(wlconfig, win); struct window *window = get_window_with_id(wlconfig, win);
@ -632,19 +790,31 @@ WaylandServer (Cursor)
{ {
window->ignoreMouse = ignoreMouse; window->ignoreMouse = ignoreMouse;
} }
} }
- (void)initializeMouseIfRequired - (void)initializeMouseIfRequired
{ {
if (!_mouseInitialized) if (!_mouseInitialized)
{
[self initializeMouse]; [self initializeMouse];
} }
}
- (void)initializeMouse - (void)initializeMouse
{ {
_mouseInitialized = YES; _mouseInitialized = YES;
wlconfig->cursor_theme =
wl_cursor_theme_load(NULL, 24, wlconfig->shm);
wlconfig->cursor_surface
= wl_compositor_create_surface(wlconfig->compositor);
// default cursor used for show/hide
struct cursor *wayland_cursor;
[self standardcursor:GSArrowCursor :(void **)&wayland_cursor];
[self setcursor:wayland_cursor];
[self mouseOptionsChanged:nil]; [self mouseOptionsChanged:nil];
[[NSDistributedNotificationCenter defaultCenter] [[NSDistributedNotificationCenter defaultCenter]
addObserver:self addObserver:self

2
configure vendored
View file

@ -7373,7 +7373,7 @@ fi
CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS" CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS"
CAIRO_CFLAGS="$CAIRO_CFLAGS" CAIRO_CFLAGS="$CAIRO_CFLAGS"
LIBS="-lwayland-client -lxkbcommon $LIBS" LIBS="-lwayland-client -lwayland-cursor -lxkbcommon $LIBS"
else else
as_fn_error $? "Invalid Cairo installation" "$LINENO" 5 as_fn_error $? "Invalid Cairo installation" "$LINENO" 5
fi fi

View file

@ -657,7 +657,7 @@ if test x"$BUILD_GRAPHICS" = "xcairo"; then
[AC_MSG_ERROR([**** No xkb_context_new in libxkbcommon. Install correct version of libxkbcommon-dev or equivalent.])]) [AC_MSG_ERROR([**** No xkb_context_new in libxkbcommon. Install correct version of libxkbcommon-dev or equivalent.])])
CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS" CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS"
CAIRO_CFLAGS="$CAIRO_CFLAGS" CAIRO_CFLAGS="$CAIRO_CFLAGS"
LIBS="-lwayland-client -lxkbcommon $LIBS" LIBS="-lwayland-client -lwayland-cursor -lxkbcommon $LIBS"
else else
AC_MSG_ERROR([Invalid Cairo installation]) AC_MSG_ERROR([Invalid Cairo installation])
fi fi