Skip to content

Commit

Permalink
Wayland: Add support for the new cursor-shape protocol
Browse files Browse the repository at this point in the history
It is currently disabled because no compositor seems to support it.
Hyprland reports it as available but using it causes Hyprland to crash.
Plasma 6 is supposed to have it but I am not installing a beta just for
this.

Typical Wayland.
  • Loading branch information
kovidgoyal committed Dec 24, 2023
1 parent 7d8c017 commit cff490f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
5 changes: 4 additions & 1 deletion gen/cursors.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def main(args: List[str]=sys.argv) -> None:
glfw_css_map = {}
css_to_enum = {}
xc_to_enum = {}
glfw_wayland = {}
for line in cursors.splitlines():
line = line.strip()
if line:
Expand All @@ -83,6 +84,7 @@ def main(args: List[str]=sys.argv) -> None:
glfw_css_map[glfw_name] = css
css_to_enum[css] = enum_name
css_names.append(css)
glfw_wayland[glfw_name] = 'WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_' + wayland.replace('-', '_').upper()
for n in names:
kitty_to_enum_map[n] = enum_name
glfw_enum.append(glfw_name)
Expand Down Expand Up @@ -112,6 +114,7 @@ def main(args: List[str]=sys.argv) -> None:

glfw_enum.append('GLFW_INVALID_CURSOR')
patch_file('glfw/glfw3.h', 'mouse cursor shapes', '\n'.join(f' {x},' for x in glfw_enum))
patch_file('glfw/wl_window.c', 'glfw to wayland mapping', '\n'.join(f' C({g}, {x});' for g, x in glfw_wayland.items()))
patch_file('glfw/wl_window.c', 'glfw to xc mapping', '\n'.join(f' C({g}, {x});' for g, x in glfw_xc_map.items()))
patch_file('glfw/x11_window.c', 'glfw to xc mapping', '\n'.join(f' {x}' for x in glfw_xfont_map))
patch_file('kitty/data-types.h', 'mouse shapes', '\n'.join(f' {x},' for x in enum_to_glfw_map))
Expand Down Expand Up @@ -139,7 +142,7 @@ def main(args: List[str]=sys.argv) -> None:
f'\t{x} PointerShape = {i}' for i, x in enumerate(enum_to_glfw_map)), start_marker='// ', end_marker='')
patch_file('tools/tui/loop/mouse.go', 'pointer shape tostring', '\n'.join(
f'''\tcase {x}: return "{x.lower().rpartition('_')[0].replace('_', '-')}"''' for x in enum_to_glfw_map), start_marker='// ', end_marker='')
patch_file('kittens/mouse_demo/main.go', 'all pointer shapes', '\n'.join(
patch_file('tools/cmd/mouse_demo/main.go', 'all pointer shapes', '\n'.join(
f'\tloop.{x},' for x in enum_to_glfw_map), start_marker='// ', end_marker='')

subprocess.check_call(['glfw/glfw.py'])
Expand Down
13 changes: 13 additions & 0 deletions glfw/wl_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,16 @@ static void seatHandleCapabilities(void* data UNUSED,
{
_glfw.wl.pointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
if (_glfw.wl.wp_cursor_shape_manager_v1) {
if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);
_glfw.wl.wp_cursor_shape_device_v1 = NULL;
_glfw.wl.wp_cursor_shape_device_v1 = wp_cursor_shape_manager_v1_get_pointer(_glfw.wl.wp_cursor_shape_manager_v1, _glfw.wl.pointer);
}
}
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
{
if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);
_glfw.wl.wp_cursor_shape_device_v1 = NULL;
wl_pointer_destroy(_glfw.wl.pointer);
_glfw.wl.pointer = NULL;
if (_glfw.wl.cursorAnimationTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
Expand Down Expand Up @@ -716,6 +723,9 @@ static void registryHandleGlobal(void* data UNUSED,
else if (is(xdg_activation_v1)) {
_glfw.wl.xdg_activation_v1 = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);
}
else if (false && is(wp_cursor_shape_manager_v1)) {
_glfw.wl.wp_cursor_shape_manager_v1 = wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, 1);
}
#undef is
}

Expand Down Expand Up @@ -950,6 +960,9 @@ void _glfwPlatformTerminate(void)
zwp_primary_selection_device_manager_v1_destroy(_glfw.wl.primarySelectionDeviceManager);
if (_glfw.wl.xdg_activation_v1)
xdg_activation_v1_destroy(_glfw.wl.xdg_activation_v1);
if (_glfw.wl.wp_cursor_shape_manager_v1)
wp_cursor_shape_manager_v1_destroy(_glfw.wl.wp_cursor_shape_manager_v1);

if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry);
if (_glfw.wl.display)
Expand Down
3 changes: 3 additions & 0 deletions glfw/wl_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-primary-selection-unstable-v1-client-protocol.h"
#include "wl_text_input.h"
#include "wayland-xdg-activation-v1-client-protocol.h"
#include "wayland-cursor-shape-v1-client-protocol.h"

#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
Expand Down Expand Up @@ -286,6 +287,8 @@ typedef struct _GLFWlibraryWayland
struct zwp_primary_selection_device_v1* primarySelectionDevice;
struct zwp_primary_selection_source_v1* dataSourceForPrimarySelection;
struct xdg_activation_v1* xdg_activation_v1;
struct wp_cursor_shape_manager_v1* wp_cursor_shape_manager_v1;
struct wp_cursor_shape_device_v1* wp_cursor_shape_device_v1;

int compositorVersion;
int seatVersion;
Expand Down
50 changes: 49 additions & 1 deletion glfw/wl_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,58 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image, bool is_opaque,
return buffer;
}

static int
glfw_cursor_shape_to_wayland_cursor_shape(GLFWCursorShape g) {
#define C(g, w) case g: return w;
switch(g) {
/* start glfw to wayland mapping (auto generated by gen-key-constants.py do not edit) */
C(GLFW_DEFAULT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
C(GLFW_TEXT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT);
C(GLFW_POINTER_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
C(GLFW_HELP_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP);
C(GLFW_WAIT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT);
C(GLFW_PROGRESS_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS);
C(GLFW_CROSSHAIR_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);
C(GLFW_CELL_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL);
C(GLFW_VERTICAL_TEXT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT);
C(GLFW_MOVE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE);
C(GLFW_E_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE);
C(GLFW_NE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE);
C(GLFW_NW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE);
C(GLFW_N_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE);
C(GLFW_SE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE);
C(GLFW_SW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE);
C(GLFW_S_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE);
C(GLFW_W_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE);
C(GLFW_EW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE);
C(GLFW_NS_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE);
C(GLFW_NESW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE);
C(GLFW_NWSE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE);
C(GLFW_ZOOM_IN_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN);
C(GLFW_ZOOM_OUT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT);
C(GLFW_ALIAS_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS);
C(GLFW_COPY_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY);
C(GLFW_NOT_ALLOWED_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED);
C(GLFW_NO_DROP_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP);
C(GLFW_GRAB_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB);
C(GLFW_GRABBING_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING);
/* end glfw to wayland mapping */
default: return -1;
}
#undef C
}

static void
setCursorImage(_GLFWwindow* window, bool on_theme_change) {
_GLFWcursorWayland defaultCursor = {.shape = GLFW_DEFAULT_CURSOR};
_GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor;
if (_glfw.wl.wp_cursor_shape_device_v1) {
int which = glfw_cursor_shape_to_wayland_cursor_shape(cursorWayland->shape);
if (which > -1) {
wp_cursor_shape_device_v1_set_shape(_glfw.wl.wp_cursor_shape_device_v1, _glfw.wl.serial, (uint32_t)which);
return;
}
}
struct wl_cursor_image* image = NULL;
struct wl_buffer* buffer = NULL;
struct wl_surface* surface = _glfw.wl.cursorSurface;
Expand Down Expand Up @@ -717,7 +765,7 @@ static void incrementCursorImage(_GLFWwindow* window)
{
if (window && window->wl.decorations.focus == CENTRAL_WINDOW && window->cursorMode != GLFW_CURSOR_HIDDEN) {
_GLFWcursor* cursor = window->wl.currentCursor;
if (cursor && cursor->wl.cursor)
if (cursor && cursor->wl.cursor && cursor->wl.cursor->image_count)
{
cursor->wl.currentImage += 1;
cursor->wl.currentImage %= cursor->wl.cursor->image_count;
Expand Down

0 comments on commit cff490f

Please sign in to comment.