Cursor: apply x/y change during resize on commit
This fixes issues with resizing clients that stick to a fixed aspect ratio during resize such as mpv.
This commit is contained in:
parent
a679743fa0
commit
c2ce893dd0
108
river/Cursor.zig
108
river/Cursor.zig
@ -131,6 +131,12 @@ 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
|
||||||
mode: Mode = .passthrough,
|
mode: Mode = .passthrough,
|
||||||
|
|
||||||
|
/// Set to whatever the current mode is when a transaction is started.
|
||||||
|
/// This is necessary to handle termination of move/resize modes properly
|
||||||
|
/// since the termination is not complete until a transaction completes and
|
||||||
|
/// View.resizeUpdatePosition() is called.
|
||||||
|
inflight_mode: Mode = .passthrough,
|
||||||
|
|
||||||
seat: *Seat,
|
seat: *Seat,
|
||||||
wlr_cursor: *wlr.Cursor,
|
wlr_cursor: *wlr.Cursor,
|
||||||
pointer_gestures: *wlr.PointerGesturesV1,
|
pointer_gestures: *wlr.PointerGesturesV1,
|
||||||
@ -334,7 +340,22 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
assert(self.pressed_count > 0);
|
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);
|
log.debug("leaving {s} mode", .{@tagName(self.mode)});
|
||||||
|
|
||||||
|
switch (self.mode) {
|
||||||
|
.passthrough => unreachable,
|
||||||
|
.down => {
|
||||||
|
// If we were in down mode, we need pass along the release event
|
||||||
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
|
},
|
||||||
|
.move => {},
|
||||||
|
.resize => |data| data.view.pending.resizing = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mode = .passthrough;
|
||||||
|
self.passthrough(event.time_msec);
|
||||||
|
|
||||||
|
server.root.applyPending();
|
||||||
} else {
|
} else {
|
||||||
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
}
|
}
|
||||||
@ -785,24 +806,6 @@ fn enterMode(cursor: *Self, mode: Mode, view: *View, image: Image) void {
|
|||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return from down/move/resize to passthrough
|
|
||||||
fn leaveMode(self: *Self, event: *wlr.Pointer.event.Button) void {
|
|
||||||
log.debug("leave {s} mode", .{@tagName(self.mode)});
|
|
||||||
|
|
||||||
switch (self.mode) {
|
|
||||||
.passthrough => unreachable,
|
|
||||||
.down => {
|
|
||||||
// If we were in down mode, we need pass along the release event
|
|
||||||
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
|
||||||
},
|
|
||||||
.move => {},
|
|
||||||
.resize => |resize| resize.view.pending.resizing = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mode = .passthrough;
|
|
||||||
self.passthrough(event.time_msec);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, delta_y: f64, unaccel_dx: f64, unaccel_dy: f64) void {
|
fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, delta_y: f64, unaccel_dx: f64, unaccel_dy: f64) void {
|
||||||
self.unhide();
|
self.unhide();
|
||||||
|
|
||||||
@ -852,25 +855,18 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
|||||||
constraint.maybeActivate();
|
constraint.maybeActivate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.move => |*data| {
|
inline .move, .resize => |*data, tag| {
|
||||||
dx += data.delta_x;
|
dx += data.delta_x;
|
||||||
dy += data.delta_y;
|
dy += data.delta_y;
|
||||||
data.delta_x = dx - @trunc(dx);
|
data.delta_x = dx - @trunc(dx);
|
||||||
data.delta_y = dy - @trunc(dy);
|
data.delta_y = dy - @trunc(dy);
|
||||||
|
|
||||||
const view = data.view;
|
if (tag == .move) {
|
||||||
view.pending.move(@floatToInt(i32, dx), @floatToInt(i32, dy));
|
data.view.pending.move(@floatToInt(i32, dx), @floatToInt(i32, dy));
|
||||||
|
} else {
|
||||||
server.root.applyPending();
|
// Modify width/height of the pending box, taking constraints into account
|
||||||
},
|
// The x/y coordinates of the view will be adjusted as needed in View.resizeCommit()
|
||||||
.resize => |*data| {
|
// based on the dimensions actually committed by the client.
|
||||||
dx += data.delta_x;
|
|
||||||
dy += data.delta_y;
|
|
||||||
data.delta_x = dx - @trunc(dx);
|
|
||||||
data.delta_y = dy - @trunc(dy);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Modify the pending box, taking constraints into account
|
|
||||||
const border_width = if (data.view.pending.ssd) server.config.border_width else 0;
|
const border_width = if (data.view.pending.ssd) server.config.border_width else 0;
|
||||||
|
|
||||||
var output_width: i32 = undefined;
|
var output_width: i32 = undefined;
|
||||||
@ -881,12 +877,13 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
|||||||
const box = &data.view.pending.box;
|
const box = &data.view.pending.box;
|
||||||
|
|
||||||
if (data.edges.left) {
|
if (data.edges.left) {
|
||||||
|
var x1 = box.x;
|
||||||
const x2 = box.x + box.width;
|
const x2 = box.x + box.width;
|
||||||
box.x += @floatToInt(i32, dx);
|
x1 += @floatToInt(i32, dx);
|
||||||
box.x = math.max(box.x, border_width);
|
x1 = math.max(x1, border_width);
|
||||||
box.x = math.max(box.x, x2 - constraints.max_width);
|
x1 = math.max(x1, x2 - constraints.max_width);
|
||||||
box.x = math.min(box.x, x2 - constraints.min_width);
|
x1 = math.min(x1, x2 - constraints.min_width);
|
||||||
box.width = x2 - box.x;
|
box.width = x2 - x1;
|
||||||
} else if (data.edges.right) {
|
} else if (data.edges.right) {
|
||||||
box.width += @floatToInt(i32, dx);
|
box.width += @floatToInt(i32, dx);
|
||||||
box.width = math.max(box.width, constraints.min_width);
|
box.width = math.max(box.width, constraints.min_width);
|
||||||
@ -895,12 +892,13 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.edges.top) {
|
if (data.edges.top) {
|
||||||
|
var y1 = box.y;
|
||||||
const y2 = box.y + box.height;
|
const y2 = box.y + box.height;
|
||||||
box.y += @floatToInt(i32, dy);
|
y1 += @floatToInt(i32, dy);
|
||||||
box.y = math.max(box.y, border_width);
|
y1 = math.max(y1, border_width);
|
||||||
box.y = math.max(box.y, y2 - constraints.max_height);
|
y1 = math.max(y1, y2 - constraints.max_height);
|
||||||
box.y = math.min(box.y, y2 - constraints.min_height);
|
y1 = math.min(y1, y2 - constraints.min_height);
|
||||||
box.height = y2 - box.y;
|
box.height = y2 - y1;
|
||||||
} else if (data.edges.bottom) {
|
} else if (data.edges.bottom) {
|
||||||
box.height += @floatToInt(i32, dy);
|
box.height += @floatToInt(i32, dy);
|
||||||
box.height = math.max(box.height, constraints.min_height);
|
box.height = math.max(box.height, constraints.min_height);
|
||||||
@ -984,12 +982,26 @@ pub fn updateState(self: *Self) void {
|
|||||||
|
|
||||||
// Keep the cursor locked to the original offset from the edges of the view.
|
// Keep the cursor locked to the original offset from the edges of the view.
|
||||||
const box = &data.view.current.box;
|
const box = &data.view.current.box;
|
||||||
const dx = data.offset_x;
|
const new_x = blk: {
|
||||||
const dy = data.offset_y;
|
if (mode == .move or data.edges.left) {
|
||||||
const lx = if (mode == .move or data.edges.left) dx + box.x else box.x + box.width - dx;
|
break :blk @intToFloat(f64, data.offset_x + box.x);
|
||||||
const ly = if (mode == .move or data.edges.top) dy + box.y else box.y + box.height - dy;
|
} else if (data.edges.right) {
|
||||||
|
break :blk @intToFloat(f64, box.x + box.width - data.offset_x);
|
||||||
|
} else {
|
||||||
|
break :blk self.wlr_cursor.x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const new_y = blk: {
|
||||||
|
if (mode == .move or data.edges.top) {
|
||||||
|
break :blk @intToFloat(f64, data.offset_y + box.y);
|
||||||
|
} else if (data.edges.bottom) {
|
||||||
|
break :blk @intToFloat(f64, box.y + box.height - data.offset_y);
|
||||||
|
} else {
|
||||||
|
break :blk self.wlr_cursor.y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.wlr_cursor.warpClosest(null, @intToFloat(f64, lx), @intToFloat(f64, ly));
|
self.wlr_cursor.warpClosest(null, new_x, new_y);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,6 +512,8 @@ pub fn applyPending(root: *Self) void {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor.inflight_mode = cursor.mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,22 +244,72 @@ pub fn destroy(view: *Self) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The change in x/y position of the view during resize cannot be determined
|
||||||
|
/// until the size of the buffer actually committed is known. Clients are permitted
|
||||||
|
/// by the protocol to take a size smaller than that requested by the compositor in
|
||||||
|
/// order to maintain an aspect ratio or similar (mpv does this for example).
|
||||||
|
pub fn resizeUpdatePosition(view: *Self, width: i32, height: i32) void {
|
||||||
|
assert(view.inflight.resizing);
|
||||||
|
|
||||||
|
const data = blk: {
|
||||||
|
var it = server.input_manager.seats.first;
|
||||||
|
while (it) |node| : (it = node.next) {
|
||||||
|
const cursor = &node.data.cursor;
|
||||||
|
if (cursor.inflight_mode == .resize and cursor.inflight_mode.resize.view == view) {
|
||||||
|
break :blk cursor.inflight_mode.resize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The view resizing state should never be set when the view is
|
||||||
|
// not the target of an interactive resize.
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.edges.left) {
|
||||||
|
view.inflight.box.x += view.current.box.width - width;
|
||||||
|
view.pending.box.x = view.inflight.box.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.edges.top) {
|
||||||
|
view.inflight.box.y += view.current.box.height - height;
|
||||||
|
view.pending.box.y = view.inflight.box.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn updateCurrent(view: *Self) void {
|
pub fn updateCurrent(view: *Self) void {
|
||||||
const config = &server.config;
|
const config = &server.config;
|
||||||
|
|
||||||
if (view.impl == .xdg_toplevel) {
|
switch (view.impl) {
|
||||||
switch (view.impl.xdg_toplevel.configure_state) {
|
.xdg_toplevel => |*xdg_toplevel| {
|
||||||
// If the configure timed out, don't update current to dimensions
|
switch (xdg_toplevel.configure_state) {
|
||||||
// that have not been committed by the client.
|
// If the configure timed out, don't update current to dimensions
|
||||||
.inflight, .acked => {
|
// that have not been committed by the client.
|
||||||
view.inflight.box.width = view.current.box.width;
|
.inflight, .acked => {
|
||||||
view.inflight.box.height = view.current.box.height;
|
if (view.inflight.resizing) {
|
||||||
view.pending.box.width = view.current.box.width;
|
view.resizeUpdatePosition(view.current.box.width, view.current.box.height);
|
||||||
view.pending.box.height = view.current.box.height;
|
}
|
||||||
},
|
view.inflight.box.width = view.current.box.width;
|
||||||
.idle, .committed => {},
|
view.inflight.box.height = view.current.box.height;
|
||||||
}
|
view.pending.box.width = view.current.box.width;
|
||||||
view.impl.xdg_toplevel.configure_state = .idle;
|
view.pending.box.height = view.current.box.height;
|
||||||
|
},
|
||||||
|
.idle, .committed => {},
|
||||||
|
}
|
||||||
|
xdg_toplevel.configure_state = .idle;
|
||||||
|
},
|
||||||
|
.xwayland_view => |xwayland_view| {
|
||||||
|
if (view.inflight.resizing) {
|
||||||
|
view.resizeUpdatePosition(
|
||||||
|
xwayland_view.xwayland_surface.width,
|
||||||
|
xwayland_view.xwayland_surface.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
view.inflight.box.width = xwayland_view.xwayland_surface.width;
|
||||||
|
view.inflight.box.height = xwayland_view.xwayland_surface.height;
|
||||||
|
view.pending.box.width = xwayland_view.xwayland_surface.width;
|
||||||
|
view.pending.box.height = xwayland_view.xwayland_surface.height;
|
||||||
|
},
|
||||||
|
.none => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
view.foreign_toplevel_handle.update();
|
view.foreign_toplevel_handle.update();
|
||||||
|
@ -317,17 +317,20 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
|||||||
self.geometry.height != old_geometry.height;
|
self.geometry.height != old_geometry.height;
|
||||||
const no_layout = view.current.output != null and view.current.output.?.layout == null;
|
const no_layout = view.current.output != null and view.current.output.?.layout == null;
|
||||||
|
|
||||||
if (size_changed and (view.current.float or no_layout) and !view.current.fullscreen) {
|
if (size_changed) {
|
||||||
log.info(
|
log.info(
|
||||||
"client initiated size change: {}x{} -> {}x{}",
|
"client initiated size change: {}x{} -> {}x{}",
|
||||||
.{ old_geometry.width, old_geometry.height, self.geometry.width, self.geometry.height },
|
.{ old_geometry.width, old_geometry.height, self.geometry.width, self.geometry.height },
|
||||||
);
|
);
|
||||||
|
if ((view.current.float or no_layout) and !view.current.fullscreen) {
|
||||||
view.current.box.width = self.geometry.width;
|
view.current.box.width = self.geometry.width;
|
||||||
view.current.box.height = self.geometry.height;
|
view.current.box.height = self.geometry.height;
|
||||||
view.pending.box.width = self.geometry.width;
|
view.pending.box.width = self.geometry.width;
|
||||||
view.pending.box.height = self.geometry.height;
|
view.pending.box.height = self.geometry.height;
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
|
} else {
|
||||||
|
log.err("client is buggy and initiated size change while tiled or fullscreen", .{});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// If the client has not yet acked our configure, we need to send a
|
// If the client has not yet acked our configure, we need to send a
|
||||||
@ -338,6 +341,10 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
|||||||
.acked => {
|
.acked => {
|
||||||
self.configure_state = .committed;
|
self.configure_state = .committed;
|
||||||
|
|
||||||
|
if (view.inflight.resizing) {
|
||||||
|
view.resizeUpdatePosition(self.geometry.width, self.geometry.height);
|
||||||
|
}
|
||||||
|
|
||||||
view.inflight.box.width = self.geometry.width;
|
view.inflight.box.width = self.geometry.width;
|
||||||
view.inflight.box.height = self.geometry.height;
|
view.inflight.box.height = self.geometry.height;
|
||||||
view.pending.box.width = self.geometry.width;
|
view.pending.box.width = self.geometry.width;
|
||||||
|
Loading…
Reference in New Issue
Block a user