code: refactor and simplify cursor move mode
This commit is contained in:
parent
11a46b80e0
commit
ac3066d8fc
1 changed files with 66 additions and 105 deletions
171
river/Cursor.zig
171
river/Cursor.zig
|
@ -33,10 +33,10 @@ const Seat = @import("Seat.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
|
|
||||||
const CursorMode = enum {
|
const Mode = enum {
|
||||||
Passthrough,
|
passthrough,
|
||||||
Move,
|
move,
|
||||||
Resize,
|
resize,
|
||||||
};
|
};
|
||||||
|
|
||||||
const default_size = 24;
|
const default_size = 24;
|
||||||
|
@ -45,21 +45,10 @@ seat: *Seat,
|
||||||
wlr_cursor: *c.wlr_cursor,
|
wlr_cursor: *c.wlr_cursor,
|
||||||
wlr_xcursor_manager: *c.wlr_xcursor_manager,
|
wlr_xcursor_manager: *c.wlr_xcursor_manager,
|
||||||
|
|
||||||
mode: CursorMode,
|
mode: Mode,
|
||||||
grabbed_view: *View,
|
|
||||||
|
|
||||||
/// Distance between cursor and top-left corner of grabbed view
|
/// The target of an in progress move or resize
|
||||||
grab_delta_x: f64,
|
target_view: ?*View,
|
||||||
grab_delta_y: f64,
|
|
||||||
|
|
||||||
/// Dimensions of the output the grabbed view is on
|
|
||||||
grab_output_width: u64,
|
|
||||||
grab_output_height: u64,
|
|
||||||
|
|
||||||
const CursorPosition = struct {
|
|
||||||
x: f64,
|
|
||||||
y: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
listen_axis: c.wl_listener,
|
listen_axis: c.wl_listener,
|
||||||
listen_button: c.wl_listener,
|
listen_button: c.wl_listener,
|
||||||
|
@ -82,10 +71,8 @@ pub fn init(self: *Self, seat: *Seat) !void {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
try self.setTheme(null, null);
|
try self.setTheme(null, null);
|
||||||
|
|
||||||
self.mode = CursorMode.Passthrough;
|
self.mode = .passthrough;
|
||||||
self.grabbed_view = undefined;
|
self.target_view = null;
|
||||||
self.grab_delta_x = 0.0;
|
|
||||||
self.grab_delta_y = 0.0;
|
|
||||||
|
|
||||||
// wlr_cursor *only* displays an image on screen. It does not move around
|
// wlr_cursor *only* displays an image on screen. It does not move around
|
||||||
// when the pointer moves. However, we can attach input devices to it, and
|
// when the pointer moves. However, we can attach input devices to it, and
|
||||||
|
@ -179,56 +166,43 @@ fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enterCursorMode(self: *Self, event: *c.wlr_event_pointer_button, view: *View, mode: CursorMode) void {
|
fn enterCursorMode(self: *Self, event: *c.wlr_event_pointer_button, view: *View, mode: Mode) void {
|
||||||
if (self.mode != CursorMode.Passthrough) return;
|
if (self.mode != .passthrough) return;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
.Passthrough => {},
|
.passthrough => {},
|
||||||
.Resize => {},
|
.resize => {},
|
||||||
|
|
||||||
.Move => {
|
.move => {
|
||||||
self.grabbed_view = view;
|
std.debug.assert(self.target_view == null);
|
||||||
|
|
||||||
// Automatically float alll views being moved by the pointer
|
self.mode = .move;
|
||||||
if (!self.grabbed_view.current.float) {
|
self.target_view = view;
|
||||||
self.grabbed_view.pending.float = true;
|
|
||||||
|
// Automatically float all views being moved by the pointer
|
||||||
|
if (!view.current.float) {
|
||||||
|
view.pending.float = true;
|
||||||
// Start a transaction to apply the pending state of the grabbed
|
// Start a transaction to apply the pending state of the grabbed
|
||||||
// view and rearrange the layout to fill the hole.
|
// view and rearrange the layout to fill the hole.
|
||||||
self.grabbed_view.output.root.arrange();
|
view.output.root.arrange();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter moving mode
|
|
||||||
self.mode = CursorMode.Move;
|
|
||||||
|
|
||||||
self.grab_delta_x = @fabs(self.wlr_cursor.x - @intToFloat(f64, self.grabbed_view.pending.box.x));
|
|
||||||
self.grab_delta_y = @fabs(self.wlr_cursor.y - @intToFloat(f64, self.grabbed_view.pending.box.y));
|
|
||||||
|
|
||||||
// Clear cursor focus, so that the surface does not receive events
|
// Clear cursor focus, so that the surface does not receive events
|
||||||
c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
|
c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
|
||||||
|
|
||||||
c.wlr_xcursor_manager_set_cursor_image(self.wlr_xcursor_manager, "move", self.wlr_cursor);
|
c.wlr_xcursor_manager_set_cursor_image(self.wlr_xcursor_manager, "move", self.wlr_cursor);
|
||||||
|
|
||||||
// Get dimension of output the grabbed view is on
|
|
||||||
var output_width_c: c_int = undefined;
|
|
||||||
var output_height_c: c_int = undefined;
|
|
||||||
c.wlr_output_effective_resolution(
|
|
||||||
self.grabbed_view.output.wlr_output,
|
|
||||||
&output_width_c,
|
|
||||||
&output_height_c,
|
|
||||||
);
|
|
||||||
self.grab_output_width = @intCast(u64, output_width_c);
|
|
||||||
self.grab_output_height = @intCast(u64, output_height_c);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leaveCursorMode(self: *Self, event: *c.wlr_event_pointer_button) void {
|
fn leaveCursorMode(self: *Self, event: *c.wlr_event_pointer_button) void {
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
.Passthrough => {},
|
.passthrough => {},
|
||||||
.Resize => {},
|
.resize => {},
|
||||||
|
|
||||||
.Move => {
|
.move => {
|
||||||
self.mode = CursorMode.Passthrough;
|
self.mode = .passthrough;
|
||||||
|
self.target_view = null;
|
||||||
|
|
||||||
// Set generic cursor image in case the application does not set one.
|
// Set generic cursor image in case the application does not set one.
|
||||||
c.wlr_xcursor_manager_set_cursor_image(
|
c.wlr_xcursor_manager_set_cursor_image(
|
||||||
|
@ -275,9 +249,9 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
// active, enter cursor mode or close view and return.
|
// active, enter cursor mode or close view and return.
|
||||||
if (self.seat.pointer_modifier) {
|
if (self.seat.pointer_modifier) {
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
c.BTN_LEFT => enterCursorMode(self, event, view, CursorMode.Move),
|
c.BTN_LEFT => enterCursorMode(self, event, view, .move),
|
||||||
c.BTN_MIDDLE => view.close(),
|
c.BTN_MIDDLE => view.close(),
|
||||||
c.BTN_RIGHT => {}, // TODO Resize
|
c.BTN_RIGHT => enterCursorMode(self, event, view, .resize),
|
||||||
|
|
||||||
// TODO Some mice have additional buttons. These
|
// TODO Some mice have additional buttons. These
|
||||||
// could also be bound to some useful action.
|
// could also be bound to some useful action.
|
||||||
|
@ -285,7 +259,7 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (self.mode != CursorMode.Passthrough) {
|
} else if (self.mode != .passthrough) {
|
||||||
// If the button is released and the current cursor mode is
|
// If the button is released and the current cursor mode is
|
||||||
// not passthrough, leave cursor mode and return.
|
// not passthrough, leave cursor mode and return.
|
||||||
leaveCursorMode(self, event);
|
leaveCursorMode(self, event);
|
||||||
|
@ -323,18 +297,17 @@ fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
|
||||||
const self = @fieldParentPtr(Self, "listen_motion_absolute", listener.?);
|
const self = @fieldParentPtr(Self, "listen_motion_absolute", listener.?);
|
||||||
const event = util.voidCast(c.wlr_event_pointer_motion_absolute, data.?);
|
const event = util.voidCast(c.wlr_event_pointer_motion_absolute, data.?);
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
CursorMode.Passthrough => {
|
.passthrough => {
|
||||||
c.wlr_cursor_warp_absolute(self.wlr_cursor, event.device, event.x, event.y);
|
c.wlr_cursor_warp_absolute(self.wlr_cursor, event.device, event.x, event.y);
|
||||||
processMotionPassthrough(self, event.time_msec);
|
processMotionPassthrough(self, event.time_msec);
|
||||||
},
|
},
|
||||||
CursorMode.Move => {
|
.move => {
|
||||||
var x_layout: f64 = undefined;
|
var lx: f64 = undefined;
|
||||||
var y_layout: f64 = undefined;
|
var ly: f64 = undefined;
|
||||||
c.wlr_cursor_absolute_to_layout_coords(self.wlr_cursor, event.device, event.x, event.y, &x_layout, &y_layout);
|
c.wlr_cursor_absolute_to_layout_coords(self.wlr_cursor, event.device, event.x, event.y, &lx, &ly);
|
||||||
var cursor: CursorPosition = processMotionMove(self, x_layout, y_layout);
|
self.processMotionMove(event.device, lx - self.wlr_cursor.x, ly - self.wlr_cursor.y);
|
||||||
_ = c.wlr_cursor_warp(self.wlr_cursor, event.device, cursor.x, cursor.y);
|
|
||||||
},
|
},
|
||||||
CursorMode.Resize => {},
|
.resize => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +317,7 @@ fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
const self = @fieldParentPtr(Self, "listen_motion", listener.?);
|
const self = @fieldParentPtr(Self, "listen_motion", listener.?);
|
||||||
const event = util.voidCast(c.wlr_event_pointer_motion, data.?);
|
const event = util.voidCast(c.wlr_event_pointer_motion, data.?);
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
CursorMode.Passthrough => {
|
.passthrough => {
|
||||||
// The cursor doesn't move unless we tell it to. The cursor automatically
|
// The cursor doesn't move unless we tell it to. The cursor automatically
|
||||||
// handles constraining the motion to the output layout, as well as any
|
// handles constraining the motion to the output layout, as well as any
|
||||||
// special configuration applied for the specific input device which
|
// special configuration applied for the specific input device which
|
||||||
|
@ -353,11 +326,8 @@ fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
c.wlr_cursor_move(self.wlr_cursor, event.device, event.delta_x, event.delta_y);
|
c.wlr_cursor_move(self.wlr_cursor, event.device, event.delta_x, event.delta_y);
|
||||||
processMotionPassthrough(self, event.time_msec);
|
processMotionPassthrough(self, event.time_msec);
|
||||||
},
|
},
|
||||||
CursorMode.Move => {
|
.move => self.processMotionMove(event.device, event.delta_x, event.delta_y),
|
||||||
var cursor: CursorPosition = processMotionMove(self, event.delta_x + self.wlr_cursor.x, event.delta_y + self.wlr_cursor.y);
|
.resize => {},
|
||||||
_ = c.wlr_cursor_warp(self.wlr_cursor, event.device, cursor.x, cursor.y);
|
|
||||||
},
|
|
||||||
CursorMode.Resize => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,47 +354,38 @@ fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursorMoveConstraints(self: *Self, _position: f64, _output: u64, _view: u64) i32 {
|
/// Move the cursor and the target view, constraining the view to the
|
||||||
const position: i32 = @floatToInt(i32, _position);
|
/// dimensions of the output.
|
||||||
const view: i32 = @intCast(i32, _view);
|
fn processMotionMove(self: *Self, device: *c.wlr_input_device, delta_x: f64, delta_y: f64) void {
|
||||||
const border_width = @intCast(i32, self.grabbed_view.output.root.server.config.border_width);
|
// Must be non-null if we are in move mode
|
||||||
const useable: i32 = @intCast(i32, _output - _view) - border_width;
|
const view = self.target_view.?;
|
||||||
|
const border_width = self.seat.input_manager.server.config.border_width;
|
||||||
|
|
||||||
var new: i32 = position;
|
var output_width: c_int = undefined;
|
||||||
if (position > useable) {
|
var output_height: c_int = undefined;
|
||||||
new = useable;
|
c.wlr_output_effective_resolution(view.output.wlr_output, &output_width, &output_height);
|
||||||
} else if (position < border_width) {
|
|
||||||
new = border_width;
|
|
||||||
}
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Moves grabbed view and returns new cursor position
|
// Set x/y of cursor and view, clamp to output dimensions
|
||||||
fn processMotionMove(self: *Self, x_in: f64, y_in: f64) CursorPosition {
|
view.pending.box.x = std.math.clamp(
|
||||||
// Get new X and Y of cursor and view.
|
view.pending.box.x + @floatToInt(i32, delta_x),
|
||||||
// Width and height of surface will stay the same.
|
@intCast(i32, border_width),
|
||||||
self.grabbed_view.pending.box.x = cursorMoveConstraints(
|
output_width - @intCast(i32, view.pending.box.width + border_width),
|
||||||
self,
|
|
||||||
x_in - self.grab_delta_x,
|
|
||||||
self.grab_output_width,
|
|
||||||
self.grabbed_view.pending.box.width,
|
|
||||||
);
|
);
|
||||||
self.grabbed_view.pending.box.y = cursorMoveConstraints(
|
view.pending.box.y = std.math.clamp(
|
||||||
self,
|
view.pending.box.y + @floatToInt(i32, delta_y),
|
||||||
y_in - self.grab_delta_y,
|
@intCast(i32, border_width),
|
||||||
self.grab_output_height,
|
output_height - @intCast(i32, view.pending.box.height + border_width),
|
||||||
self.grabbed_view.pending.box.height,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply new pending state (no need for a transaction as size didn't change)
|
c.wlr_cursor_move(
|
||||||
self.grabbed_view.current = self.grabbed_view.pending;
|
self.wlr_cursor,
|
||||||
|
device,
|
||||||
|
@intToFloat(f64, view.pending.box.x - view.current.box.x),
|
||||||
|
@intToFloat(f64, view.pending.box.y - view.current.box.y),
|
||||||
|
);
|
||||||
|
|
||||||
// This function returns the cursor position so that the calling function
|
// Apply new pending state (no need for a configure as size didn't change)
|
||||||
// can do the cursor movement.
|
view.current = view.pending;
|
||||||
return .{
|
|
||||||
.x = @intToFloat(f64, self.grabbed_view.current.box.x) + self.grab_delta_x,
|
|
||||||
.y = @intToFloat(f64, self.grabbed_view.current.box.y) + self.grab_delta_y,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processMotionPassthrough(self: *Self, time: u32) void {
|
fn processMotionPassthrough(self: *Self, time: u32) void {
|
||||||
|
|
Loading…
Reference in a new issue