Cursor: fix down mode motion events for subsurfaces
There are a couple of TODOs here that are not worth resolving until after moving to the scene graph api.
This commit is contained in:
parent
9c2e3b6738
commit
ea4bd5e54b
143
river/Cursor.zig
143
river/Cursor.zig
@ -42,7 +42,19 @@ const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
|
|||||||
|
|
||||||
const Mode = union(enum) {
|
const Mode = union(enum) {
|
||||||
passthrough: void,
|
passthrough: void,
|
||||||
down: *View,
|
down: struct {
|
||||||
|
// TODO: To handle the surface with pointer focus being moved during
|
||||||
|
// down mode we need to store the starting location of the surface as
|
||||||
|
// well and take that into account. This is currently not at all easy
|
||||||
|
// to do, but moing to the wlroots scene graph will allow us to fix this.
|
||||||
|
|
||||||
|
// Initial cursor position in layout coordinates
|
||||||
|
lx: f64,
|
||||||
|
ly: f64,
|
||||||
|
// Initial cursor position in surface-local coordinates
|
||||||
|
sx: f64,
|
||||||
|
sy: f64,
|
||||||
|
},
|
||||||
move: struct {
|
move: struct {
|
||||||
view: *View,
|
view: *View,
|
||||||
/// View coordinates are stored as i32s as they are in logical pixels.
|
/// View coordinates are stored as i32s as they are in logical pixels.
|
||||||
@ -204,8 +216,7 @@ pub fn setTheme(self: *Self, theme: ?[*:0]const u8, _size: ?u32) !void {
|
|||||||
|
|
||||||
pub fn handleViewUnmap(self: *Self, view: *View) void {
|
pub fn handleViewUnmap(self: *Self, view: *View) void {
|
||||||
if (switch (self.mode) {
|
if (switch (self.mode) {
|
||||||
.passthrough => false,
|
.passthrough, .down => false,
|
||||||
.down => |target_view| target_view == view,
|
|
||||||
.move => |data| data.view == view,
|
.move => |data| data.view == view,
|
||||||
.resize => |data| data.view == view,
|
.resize => |data| data.view == view,
|
||||||
}) {
|
}) {
|
||||||
@ -255,29 +266,33 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
|
|
||||||
self.seat.handleActivity();
|
self.seat.handleActivity();
|
||||||
|
|
||||||
if (event.state == .pressed) {
|
if (event.state == .released) {
|
||||||
self.pressed_count += 1;
|
|
||||||
} else {
|
|
||||||
std.debug.assert(self.pressed_count > 0);
|
std.debug.assert(self.pressed_count > 0);
|
||||||
self.pressed_count -= 1;
|
self.pressed_count -= 1;
|
||||||
if (self.pressed_count == 0 and self.mode != .passthrough) {
|
if (self.pressed_count == 0 and self.mode != .passthrough) {
|
||||||
self.leaveMode(event);
|
self.leaveMode(event);
|
||||||
return;
|
} else {
|
||||||
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(event.state == .pressed);
|
||||||
|
self.pressed_count += 1;
|
||||||
|
|
||||||
|
if (self.pressed_count > 1) {
|
||||||
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.surfaceAt()) |result| {
|
if (self.surfaceAt()) |result| {
|
||||||
switch (result.parent) {
|
switch (result.parent) {
|
||||||
.view => |view| {
|
.view => |view| {
|
||||||
// If a view has been clicked on, give that view keyboard focus and
|
// If there is an active mapping for this button which is
|
||||||
// perhaps enter move/resize mode.
|
// handled we are done here
|
||||||
if (event.state == .pressed and self.pressed_count == 1) {
|
if (self.handlePointerMapping(event, view)) return;
|
||||||
// If there is an active mapping for this button which is
|
// Otherwise focus the view
|
||||||
// handled we are done here
|
self.seat.focus(view);
|
||||||
if (self.handlePointerMapping(event, view)) return;
|
|
||||||
// Otherwise enter cursor down mode, giving keyboard focus
|
|
||||||
self.enterMode(.down, view);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.layer_surface => |layer_surface| {
|
.layer_surface => |layer_surface| {
|
||||||
self.seat.focusOutput(layer_surface.output);
|
self.seat.focusOutput(layer_surface.output);
|
||||||
@ -288,11 +303,21 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
} else {
|
} else {
|
||||||
self.seat.focus(null);
|
self.seat.focus(null);
|
||||||
}
|
}
|
||||||
server.root.startTransaction();
|
|
||||||
},
|
},
|
||||||
.xwayland_unmanaged => assert(build_options.xwayland),
|
.xwayland_unmanaged => assert(build_options.xwayland),
|
||||||
}
|
}
|
||||||
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
|
|
||||||
|
self.mode = .{
|
||||||
|
.down = .{
|
||||||
|
.lx = self.wlr_cursor.x,
|
||||||
|
.ly = self.wlr_cursor.y,
|
||||||
|
.sx = result.sx,
|
||||||
|
.sy = result.sy,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
server.root.startTransaction();
|
||||||
} else if (server.root.output_layout.outputAt(self.wlr_cursor.x, self.wlr_cursor.y)) |wlr_output| {
|
} else if (server.root.output_layout.outputAt(self.wlr_cursor.x, self.wlr_cursor.y)) |wlr_output| {
|
||||||
// If the user clicked on empty space of an output, focus it.
|
// If the user clicked on empty space of an output, focus it.
|
||||||
const output = @intToPtr(*Output, wlr_output.data);
|
const output = @intToPtr(*Output, wlr_output.data);
|
||||||
@ -680,52 +705,42 @@ fn surfaceAtFilter(view: *View, filter_tags: u32) bool {
|
|||||||
return view.surface != null and view.current.tags & filter_tags != 0;
|
return view.surface != null and view.current.tags & filter_tags != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enterMode(self: *Self, mode: std.meta.Tag((Mode)), view: *View) void {
|
pub fn enterMode(self: *Self, mode: enum { move, resize }, view: *View) void {
|
||||||
log.debug("enter {s} cursor mode", .{@tagName(mode)});
|
log.debug("enter {s} cursor mode", .{@tagName(mode)});
|
||||||
|
|
||||||
self.seat.focus(view);
|
self.seat.focus(view);
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
.passthrough => unreachable,
|
.move => self.mode = .{ .move = .{ .view = view } },
|
||||||
.down => {
|
.resize => {
|
||||||
self.mode = .{ .down = view };
|
const cur_box = &view.current.box;
|
||||||
server.root.startTransaction();
|
self.mode = .{ .resize = .{
|
||||||
},
|
.view = view,
|
||||||
.move, .resize => {
|
.offset_x = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x),
|
||||||
switch (mode) {
|
.offset_y = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y),
|
||||||
.passthrough, .down => unreachable,
|
} };
|
||||||
.move => self.mode = .{ .move = .{ .view = view } },
|
view.setResizing(true);
|
||||||
.resize => {
|
|
||||||
const cur_box = &view.current.box;
|
|
||||||
self.mode = .{ .resize = .{
|
|
||||||
.view = view,
|
|
||||||
.offset_x = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x),
|
|
||||||
.offset_y = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y),
|
|
||||||
} };
|
|
||||||
view.setResizing(true);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically float all views being moved by the pointer, if
|
|
||||||
// their dimensions are set by a layout generator. If however the views
|
|
||||||
// are unarranged, leave them as non-floating so the next active
|
|
||||||
// layout can affect them.
|
|
||||||
if (!view.current.float and view.output.current.layout != null) {
|
|
||||||
view.pending.float = true;
|
|
||||||
view.float_box = view.current.box;
|
|
||||||
view.applyPending();
|
|
||||||
} else {
|
|
||||||
// The View.applyPending() call in the other branch starts
|
|
||||||
// the transaction needed after the seat.focus() call above.
|
|
||||||
server.root.startTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear cursor focus, so that the surface does not receive events
|
|
||||||
self.seat.wlr_seat.pointerNotifyClearFocus();
|
|
||||||
|
|
||||||
self.setImage(if (mode == .move) .move else .@"se-resize");
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Automatically float all views being moved by the pointer, if
|
||||||
|
// their dimensions are set by a layout generator. If however the views
|
||||||
|
// are unarranged, leave them as non-floating so the next active
|
||||||
|
// layout can affect them.
|
||||||
|
if (!view.current.float and view.output.current.layout != null) {
|
||||||
|
view.pending.float = true;
|
||||||
|
view.float_box = view.current.box;
|
||||||
|
view.applyPending();
|
||||||
|
} else {
|
||||||
|
// The View.applyPending() call in the other branch starts
|
||||||
|
// the transaction needed after the seat.focus() call above.
|
||||||
|
server.root.startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cursor focus, so that the surface does not receive events
|
||||||
|
self.seat.wlr_seat.pointerNotifyClearFocus();
|
||||||
|
|
||||||
|
self.setImage(if (mode == .move) .move else .@"se-resize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return from down/move/resize to passthrough
|
/// Return from down/move/resize to passthrough
|
||||||
@ -783,14 +798,12 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
|||||||
self.checkFocusFollowsCursor();
|
self.checkFocusFollowsCursor();
|
||||||
self.passthrough(time);
|
self.passthrough(time);
|
||||||
},
|
},
|
||||||
.down => |view| {
|
.down => |down| {
|
||||||
self.wlr_cursor.move(device, dx, dy);
|
self.wlr_cursor.move(device, dx, dy);
|
||||||
// This takes surface-local coordinates
|
|
||||||
const output_box = server.root.output_layout.getBox(view.output.wlr_output).?;
|
|
||||||
self.seat.wlr_seat.pointerNotifyMotion(
|
self.seat.wlr_seat.pointerNotifyMotion(
|
||||||
time,
|
time,
|
||||||
self.wlr_cursor.x - @intToFloat(f64, output_box.x + view.current.box.x - view.surface_box.x),
|
down.sx + (self.wlr_cursor.x - down.lx),
|
||||||
self.wlr_cursor.y - @intToFloat(f64, output_box.y + view.current.box.y - view.surface_box.y),
|
down.sy + (self.wlr_cursor.y - down.ly),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
.move => |*data| {
|
.move => |*data| {
|
||||||
@ -882,9 +895,11 @@ fn shouldPassthrough(self: Self) bool {
|
|||||||
// target view.
|
// target view.
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
.down => |target| {
|
.down => {
|
||||||
// The target view is no longer visible
|
// TODO: It's hard to determine from the target surface alone whether
|
||||||
return target.current.tags & target.output.current.tags == 0;
|
// the surface is visible or not currently. Switching to the wlroots
|
||||||
|
// scene graph will fix this, but for now just don't bother.
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
.resize, .move => {
|
.resize, .move => {
|
||||||
const target = if (self.mode == .resize) self.mode.resize.view else self.mode.move.view;
|
const target = if (self.mode == .resize) self.mode.resize.view else self.mode.move.view;
|
||||||
|
Loading…
Reference in New Issue
Block a user