support cursor-shape-v1
This commit is contained in:
parent
49125b9c01
commit
d93e127db4
2 changed files with 44 additions and 15 deletions
|
@ -11,11 +11,13 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
const scanner = Scanner.create(b, .{});
|
||||
scanner.addCustomProtocol("protocol/wlr-layer-shell-unstable-v1.xml");
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml"); // Dependency of layer-shell.
|
||||
scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml");
|
||||
scanner.addSystemProtocol("unstable/tablet/tablet-unstable-v2.xml"); // Dependency of cursor-shape.
|
||||
scanner.generate("wp_cursor_shape_manager_v1", 1);
|
||||
scanner.generate("wl_compositor", 5);
|
||||
scanner.generate("wl_shm", 1);
|
||||
scanner.generate("zwlr_layer_shell_v1", 3);
|
||||
scanner.generate("xdg_wm_base", 5); // Needed as a dependency of the layershell.
|
||||
scanner.generate("wl_seat", 8);
|
||||
scanner.generate("wl_output", 4);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ const fcft = @import("fcft");
|
|||
const xkb = @import("xkbcommon");
|
||||
const wayland = @import("wayland");
|
||||
const wl = wayland.client.wl;
|
||||
const wp = wayland.client.wp;
|
||||
const zwlr = wayland.client.zwlr;
|
||||
|
||||
const Frontend = @import("Frontend.zig");
|
||||
|
@ -216,8 +217,6 @@ const TextView = struct {
|
|||
};
|
||||
|
||||
const Seat = struct {
|
||||
const CursorShape = enum { none, arrow, hand };
|
||||
|
||||
w: *Wayland,
|
||||
|
||||
wl_seat: *wl.Seat,
|
||||
|
@ -228,9 +227,10 @@ const Seat = struct {
|
|||
|
||||
// Pointer related objects.
|
||||
wl_pointer: ?*wl.Pointer = null,
|
||||
cursor_shape_device: ?*wp.CursorShapeDeviceV1 = null,
|
||||
pointer_x: u31 = 0,
|
||||
pointer_y: u31 = 0,
|
||||
cursor_shape: CursorShape = .none,
|
||||
cursor_shape: ?wp.CursorShapeDeviceV1.Shape = null,
|
||||
cursor_theme: ?*wl.CursorTheme = null,
|
||||
cursor_surface: ?*wl.Surface = null,
|
||||
last_enter_serial: u32 = undefined,
|
||||
|
@ -270,12 +270,16 @@ const Seat = struct {
|
|||
|
||||
fn bindPointer(self: *Seat) !void {
|
||||
if (self.wl_pointer != null) return;
|
||||
debug.assert(self.cursor_shape == null);
|
||||
self.wl_pointer = try self.wl_seat.getPointer();
|
||||
self.wl_pointer.?.setListener(*Seat, pointerListener, self);
|
||||
if (self.w.cursor_shape_manager) |csm| {
|
||||
self.cursor_shape_device = try csm.getPointer(self.wl_pointer.?);
|
||||
}
|
||||
}
|
||||
|
||||
fn releasePointer(self: *Seat) void {
|
||||
self.cursor_shape = .none;
|
||||
self.cursor_shape = null;
|
||||
self.press_hotspot = null;
|
||||
if (self.cursor_theme) |t| {
|
||||
t.destroy();
|
||||
|
@ -289,6 +293,10 @@ const Seat = struct {
|
|||
p.release();
|
||||
self.wl_pointer = null;
|
||||
}
|
||||
if (self.cursor_shape_device) |csd| {
|
||||
csd.destroy();
|
||||
self.cursor_shape_device = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn pointerListener(_: *wl.Pointer, event: wl.Pointer.Event, self: *Seat) void {
|
||||
|
@ -298,6 +306,7 @@ const Seat = struct {
|
|||
|
||||
switch (event) {
|
||||
.enter => |ev| self.updatePointer(ev.surface_x, ev.surface_y, ev.serial),
|
||||
.leave => self.cursor_shape = null,
|
||||
.motion => |ev| self.updatePointer(ev.surface_x, ev.surface_y, null),
|
||||
.button => |ev| {
|
||||
// Only activating a button on release is the better UX, IMO.
|
||||
|
@ -335,20 +344,31 @@ const Seat = struct {
|
|||
// something to go wrong there and it's not exactly vital to our
|
||||
// operation here, so we can roll without setting the cursor.
|
||||
if (self.w.surface.?.hotspotFromPoint(self.pointer_x, self.pointer_y) != null) {
|
||||
self.setCursor(.hand) catch {};
|
||||
self.setCursor(.pointer) catch {};
|
||||
} else {
|
||||
self.setCursor(.arrow) catch {};
|
||||
self.setCursor(.default) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
fn setCursor(self: *Seat, shape: CursorShape) !void {
|
||||
if (self.cursor_shape == shape) return;
|
||||
fn setCursor(self: *Seat, shape: wp.CursorShapeDeviceV1.Shape) !void {
|
||||
if (self.cursor_shape) |cs| {
|
||||
if (cs == shape) return;
|
||||
}
|
||||
self.cursor_shape = shape;
|
||||
|
||||
const name = switch (shape) {
|
||||
.none => unreachable,
|
||||
.arrow => "default",
|
||||
.hand => "pointer",
|
||||
};
|
||||
log.debug("Setting cursor: {s}", .{@tagName(shape)});
|
||||
|
||||
// If the Wayland server supports the CursorShapeV1 protocol, use that
|
||||
// to set the cursor. Otherwise we need to go through wl-cursor.
|
||||
if (self.cursor_shape_device) |csd| {
|
||||
csd.setShape(self.last_enter_serial, shape);
|
||||
} else {
|
||||
try self.setCursorSurface(shape);
|
||||
}
|
||||
}
|
||||
|
||||
fn setCursorSurface(self: *Seat, shape: wp.CursorShapeDeviceV1.Shape) !void {
|
||||
const name = @tagName(shape);
|
||||
|
||||
const scale = 1; // TODO
|
||||
const cursor_size = 24 * scale;
|
||||
|
@ -1308,6 +1328,7 @@ font_regular: ?*fcft.Font = null,
|
|||
display: *wl.Display = undefined,
|
||||
registry: ?*wl.Registry = null,
|
||||
layer_shell: ?*zwlr.LayerShellV1 = null,
|
||||
cursor_shape_manager: ?*wp.CursorShapeManagerV1 = null,
|
||||
compositor: ?*wl.Compositor = null,
|
||||
shm: ?*wl.Shm = null,
|
||||
seats: std.TailQueue(Seat) = .{},
|
||||
|
@ -1383,6 +1404,7 @@ pub fn deinit(self: *Wayland) void {
|
|||
|
||||
self.buffer_pool.deinit(self.config.alloc);
|
||||
if (self.layer_shell) |ls| ls.destroy();
|
||||
if (self.cursor_shape_manager) |csm| csm.destroy();
|
||||
if (self.compositor) |cmp| cmp.destroy();
|
||||
if (self.shm) |sm| sm.destroy();
|
||||
|
||||
|
@ -1572,6 +1594,11 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Way
|
|||
self.abort(error.OutOfMemory);
|
||||
return;
|
||||
};
|
||||
} else if (mem.orderZ(u8, ev.interface, wp.CursorShapeManagerV1.getInterface().name) == .eq) {
|
||||
self.cursor_shape_manager = registry.bind(ev.name, wp.CursorShapeManagerV1, 1) catch {
|
||||
self.abort(error.OutOfMemory);
|
||||
return;
|
||||
};
|
||||
} else if (mem.orderZ(u8, ev.interface, wl.Compositor.getInterface().name) == .eq) {
|
||||
self.compositor = registry.bind(ev.name, wl.Compositor, 4) catch {
|
||||
self.abort(error.OutOfMemory);
|
||||
|
|
Loading…
Reference in a new issue