Cursor: restore previous image on unhide
If client A has an xdg_popup open and the user moves the cursor over a surface of client B and waits for the cursor to be hidden after a timeout, the cursor will not be shown on movement until the (invisible) cursor is moved back into a surface of client A or somewhere the compositor is responsible for rendering the cursor. This is due to the (flawed) xdg popup grab interface of wlroots which prevents wlr_seat_pointer_notify_enter() from sending events to clients other than the one with the active xdg popup. Closes: https://codeberg.org/river/river/issues/1192
This commit is contained in:
parent
543697847f
commit
8490558b8b
@ -108,6 +108,19 @@ const LayoutPoint = struct {
|
|||||||
ly: f64,
|
ly: f64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Image = union(enum) {
|
||||||
|
/// No cursor image
|
||||||
|
none,
|
||||||
|
/// Name of the current Xcursor shape
|
||||||
|
xcursor: [*:0]const u8,
|
||||||
|
/// Cursor surface configured by a client
|
||||||
|
client: struct {
|
||||||
|
surface: *wlr.Surface,
|
||||||
|
hotspot_x: i32,
|
||||||
|
hotspot_y: i32,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const log = std.log.scoped(.cursor);
|
const log = std.log.scoped(.cursor);
|
||||||
|
|
||||||
/// Current cursor mode as well as any state needed to implement that mode
|
/// Current cursor mode as well as any state needed to implement that mode
|
||||||
@ -124,9 +137,8 @@ wlr_cursor: *wlr.Cursor,
|
|||||||
|
|
||||||
/// Xcursor manager for the currently configured Xcursor theme.
|
/// Xcursor manager for the currently configured Xcursor theme.
|
||||||
xcursor_manager: *wlr.XcursorManager,
|
xcursor_manager: *wlr.XcursorManager,
|
||||||
/// Name of the current Xcursor shape, or null if a client has configured a
|
image: Image = .none,
|
||||||
/// surface to be used as the cursor shape instead.
|
image_surface_destroy: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleImageSurfaceDestroy),
|
||||||
xcursor_name: ?[*:0]const u8 = null,
|
|
||||||
|
|
||||||
/// Number of distinct buttons currently pressed
|
/// Number of distinct buttons currently pressed
|
||||||
pressed_count: u32 = 0,
|
pressed_count: u32 = 0,
|
||||||
@ -286,18 +298,40 @@ pub fn setTheme(cursor: *Cursor, theme: ?[*:0]const u8, _size: ?u32) !void {
|
|||||||
cursor.xcursor_manager.destroy();
|
cursor.xcursor_manager.destroy();
|
||||||
cursor.xcursor_manager = xcursor_manager;
|
cursor.xcursor_manager = xcursor_manager;
|
||||||
|
|
||||||
if (cursor.xcursor_name) |name| {
|
switch (cursor.image) {
|
||||||
cursor.setXcursor(name);
|
.none, .client => {},
|
||||||
|
.xcursor => |name| cursor.wlr_cursor.setXcursor(xcursor_manager, name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setXcursor(cursor: *Cursor, name: [*:0]const u8) void {
|
pub fn setImage(cursor: *Cursor, image: Image) void {
|
||||||
cursor.wlr_cursor.setXcursor(cursor.xcursor_manager, name);
|
switch (cursor.image) {
|
||||||
cursor.xcursor_name = name;
|
.none, .xcursor => {},
|
||||||
|
.client => {
|
||||||
|
cursor.image_surface_destroy.link.remove();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cursor.image = image;
|
||||||
|
switch (cursor.image) {
|
||||||
|
.none => cursor.wlr_cursor.unsetImage(),
|
||||||
|
.xcursor => |name| cursor.wlr_cursor.setXcursor(cursor.xcursor_manager, name),
|
||||||
|
.client => |client| {
|
||||||
|
cursor.wlr_cursor.setSurface(client.surface, client.hotspot_x, client.hotspot_y);
|
||||||
|
client.surface.events.destroy.add(&cursor.image_surface_destroy);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleImageSurfaceDestroy(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
||||||
|
const cursor: *Cursor = @fieldParentPtr("image_surface_destroy", listener);
|
||||||
|
// wlroots calls wlr_cursor_unset_image() automatically
|
||||||
|
// when the cursor surface is destroyed.
|
||||||
|
cursor.image = .none;
|
||||||
|
cursor.image_surface_destroy.link.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clearFocus(cursor: *Cursor) void {
|
fn clearFocus(cursor: *Cursor) void {
|
||||||
cursor.setXcursor("default");
|
cursor.setImage(.{ .xcursor = "default" });
|
||||||
cursor.seat.wlr_seat.pointerNotifyClearFocus();
|
cursor.seat.wlr_seat.pointerNotifyClearFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,8 +774,15 @@ fn handleRequestSetCursor(
|
|||||||
// on the output that it's currently on and continue to do so as the
|
// on the output that it's currently on and continue to do so as the
|
||||||
// cursor moves between outputs.
|
// cursor moves between outputs.
|
||||||
log.debug("focused client set cursor", .{});
|
log.debug("focused client set cursor", .{});
|
||||||
cursor.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
|
if (event.surface) |surface| {
|
||||||
cursor.xcursor_name = null;
|
cursor.setImage(.{ .client = .{
|
||||||
|
.surface = surface,
|
||||||
|
.hotspot_x = event.hotspot_x,
|
||||||
|
.hotspot_y = event.hotspot_y,
|
||||||
|
} });
|
||||||
|
} else {
|
||||||
|
cursor.setImage(.none);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,8 +798,6 @@ pub fn hide(cursor: *Cursor) void {
|
|||||||
|
|
||||||
cursor.hidden = true;
|
cursor.hidden = true;
|
||||||
cursor.wlr_cursor.unsetImage();
|
cursor.wlr_cursor.unsetImage();
|
||||||
cursor.xcursor_name = null;
|
|
||||||
cursor.seat.wlr_seat.pointerNotifyClearFocus();
|
|
||||||
cursor.hide_cursor_timer.timerUpdate(0) catch {
|
cursor.hide_cursor_timer.timerUpdate(0) catch {
|
||||||
log.err("failed to update cursor hide timeout", .{});
|
log.err("failed to update cursor hide timeout", .{});
|
||||||
};
|
};
|
||||||
@ -770,6 +809,7 @@ pub fn unhide(cursor: *Cursor) void {
|
|||||||
};
|
};
|
||||||
if (!cursor.hidden) return;
|
if (!cursor.hidden) return;
|
||||||
cursor.hidden = false;
|
cursor.hidden = false;
|
||||||
|
cursor.setImage(cursor.image);
|
||||||
cursor.updateState();
|
cursor.updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +908,7 @@ fn computeEdges(cursor: *const Cursor, view: *const View) wlr.Edges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enterMode(cursor: *Cursor, mode: Mode, view: *View, xcursor_name: [*:0]const u8) void {
|
fn enterMode(cursor: *Cursor, mode: Mode, view: *View, xcursor: [*:0]const u8) void {
|
||||||
assert(cursor.mode == .passthrough or cursor.mode == .down);
|
assert(cursor.mode == .passthrough or cursor.mode == .down);
|
||||||
assert(mode == .move or mode == .resize);
|
assert(mode == .move or mode == .resize);
|
||||||
|
|
||||||
@ -884,7 +924,7 @@ fn enterMode(cursor: *Cursor, mode: Mode, view: *View, xcursor_name: [*:0]const
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.seat.wlr_seat.pointerNotifyClearFocus();
|
cursor.seat.wlr_seat.pointerNotifyClearFocus();
|
||||||
cursor.setXcursor(xcursor_name);
|
cursor.setImage(.{ .xcursor = xcursor });
|
||||||
|
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
}
|
}
|
||||||
|
@ -519,7 +519,7 @@ fn handleRequestSetCursorShape(
|
|||||||
// actually has pointer focus first.
|
// actually has pointer focus first.
|
||||||
if (focused_client == event.seat_client) {
|
if (focused_client == event.seat_client) {
|
||||||
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
|
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
|
||||||
seat.cursor.setXcursor(name);
|
seat.cursor.setImage(.{ .xcursor = name });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user