From 54b2b172d8c11684e0999946e4e0c099cf97ba54 Mon Sep 17 00:00:00 2001 From: Riccardo Canalicchio Date: Sat, 18 Dec 2021 16:17:30 +0100 Subject: [PATCH] Wayland backend: cursor icon functions --- Headers/cairo/WaylandCairoShmSurface.h | 15 ++ Headers/wayland/WaylandServer.h | 29 +++- Source/cairo/WaylandCairoShmSurface.m | 39 +++-- Source/wayland/WaylandServer+Cursor.m | 200 +++++++++++++++++++++++-- configure | 2 +- configure.ac | 2 +- 6 files changed, 245 insertions(+), 42 deletions(-) diff --git a/Headers/cairo/WaylandCairoShmSurface.h b/Headers/cairo/WaylandCairoShmSurface.h index bf293ec..3f200e7 100644 --- a/Headers/cairo/WaylandCairoShmSurface.h +++ b/Headers/cairo/WaylandCairoShmSurface.h @@ -30,6 +30,21 @@ #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 { } diff --git a/Headers/wayland/WaylandServer.h b/Headers/wayland/WaylandServer.h index 65083d3..1e13d8f 100755 --- a/Headers/wayland/WaylandServer.h +++ b/Headers/wayland/WaylandServer.h @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -60,6 +61,15 @@ struct pointer uint32_t serial; 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 @@ -73,6 +83,7 @@ typedef struct _WaylandConfig struct wl_keyboard *keyboard; struct xdg_wm_base *wm_base; struct zwlr_layer_shell_v1 *layer_shell; + int seat_version; struct wl_list output_list; int output_count; @@ -80,7 +91,20 @@ typedef struct _WaylandConfig int window_count; 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; + float mouse_scroll_multiplier; + +// keyboard struct xkb_context *xkb_context; struct { @@ -92,9 +116,6 @@ typedef struct _WaylandConfig } xkb; int modifiers; - int seat_version; - - float mouse_scroll_multiplier; } WaylandConfig; struct output @@ -147,6 +168,8 @@ struct window CairoSurface *wcs; }; +struct window *get_window_with_id(WaylandConfig *wlconfig, int winid); + @interface WaylandServer : GSDisplayServer { WaylandConfig *wlconfig; diff --git a/Source/cairo/WaylandCairoShmSurface.m b/Source/cairo/WaylandCairoShmSurface.m index 79ef61d..712816e 100644 --- a/Source/cairo/WaylandCairoShmSurface.m +++ b/Source/cairo/WaylandCairoShmSurface.m @@ -44,17 +44,6 @@ #include #include -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 cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32; @@ -135,11 +124,11 @@ createPoolFile(off_t size) return fd; } -static struct pool_buffer * -createBufferForWindow(struct window * window, struct wl_shm *shm) +struct pool_buffer * +createShmBuffer(int width, int height, struct wl_shm *shm) { - uint32_t stride = cairo_format_stride_for_width(cairo_fmt, window->width); - size_t size = stride * window->height; + uint32_t stride = cairo_format_stride_for_width(cairo_fmt, width); + size_t size = stride * height; 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->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); wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); } + else + { + return NULL; + } buf->data = data; buf->size = size; - buf->width = window->width; - buf->height = window->height; - buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, window->width, window->height, stride); + buf->width = width; + buf->height = height; + buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, width, height, stride); - wl_shm_pool_destroy(buf->pool); + if(buf->pool) + { + wl_shm_pool_destroy(buf->pool); + } return buf; } - @implementation WaylandCairoShmSurface { struct pool_buffer *pbuffer; @@ -188,7 +183,7 @@ createBufferForWindow(struct window * window, struct wl_shm *shm) gsDevice = device; - pbuffer = createBufferForWindow(window, window->wlconfig->shm); + pbuffer = createShmBuffer(window->width, window->height, window->wlconfig->shm); if (pbuffer == NULL) { diff --git a/Source/wayland/WaylandServer+Cursor.m b/Source/wayland/WaylandServer+Cursor.m index 5a4731f..b772a2a 100755 --- a/Source/wayland/WaylandServer+Cursor.m +++ b/Source/wayland/WaylandServer+Cursor.m @@ -29,9 +29,13 @@ #import #import #import +#import +#import +#import #import #import #include +#include "wayland-cursor.h" // XXX should this be configurable by the user? #define DOUBLECLICK_DELAY 300 @@ -53,11 +57,13 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, { return; } + [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; + + NSDebugLog(@"[%d] pointer_handle_enter",window->window_id); float sx = wl_fixed_to_double(sx_w); float sy = wl_fixed_to_double(sy_w); - [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; 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.y = sy; + wlconfig->pointer.serial = serial; } static void @@ -141,7 +148,7 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, [GSCurrentServer() postEvent:event atStart:NO]; 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; struct window *focused_window = wlconfig->pointer.focus; - if(window->ignoreMouse) + if(focused_window->ignoreMouse) { 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; + [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; 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_state = state; wlconfig->pointer.last_timestamp = timestamp; + wlconfig->pointer.serial = serial; } @@ -586,22 +595,132 @@ WaylandServer (Cursor) - (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 { - 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 { - 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 { - 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 @@ -613,38 +732,89 @@ WaylandServer (Cursor) - (void) recolorcursor:(NSColor *)fg :(NSColor *)bg :(void*) cid { + // TODO recolorcursor NSDebugLog(@"recolorcursor"); } - (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 { - 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 { struct window *window = get_window_with_id(wlconfig, win); - if(window) - { - window->ignoreMouse = ignoreMouse; - } - + if (window) + { + window->ignoreMouse = ignoreMouse; + } } - (void)initializeMouseIfRequired { if (!_mouseInitialized) - [self initializeMouse]; + { + [self initializeMouse]; + } } - (void)initializeMouse { _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]; [[NSDistributedNotificationCenter defaultCenter] addObserver:self diff --git a/configure b/configure index be75c25..cd97dc9 100755 --- a/configure +++ b/configure @@ -7373,7 +7373,7 @@ fi CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS" CAIRO_CFLAGS="$CAIRO_CFLAGS" - LIBS="-lwayland-client -lxkbcommon $LIBS" + LIBS="-lwayland-client -lwayland-cursor -lxkbcommon $LIBS" else as_fn_error $? "Invalid Cairo installation" "$LINENO" 5 fi diff --git a/configure.ac b/configure.ac index a384e68..88a80f5 100644 --- a/configure.ac +++ b/configure.ac @@ -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.])]) CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS" CAIRO_CFLAGS="$CAIRO_CFLAGS" - LIBS="-lwayland-client -lxkbcommon $LIBS" + LIBS="-lwayland-client -lwayland-cursor -lxkbcommon $LIBS" else AC_MSG_ERROR([Invalid Cairo installation]) fi