Cursor: implement initial touch support
Still TODO are: - Touch support for drags - Mapping input devices to outputs (necessary for good multi-monitor touch support) Co-authored-by: Daan Vanoverloop <daan@vanoverloop.xyz>
This commit is contained in:
parent
bd03484b28
commit
2eb013e214
135
river/Cursor.zig
135
river/Cursor.zig
@ -129,6 +129,14 @@ swipe_update: wl.Listener(*wlr.Pointer.event.SwipeUpdate) =
|
|||||||
swipe_end: wl.Listener(*wlr.Pointer.event.SwipeEnd) =
|
swipe_end: wl.Listener(*wlr.Pointer.event.SwipeEnd) =
|
||||||
wl.Listener(*wlr.Pointer.event.SwipeEnd).init(handleSwipeEnd),
|
wl.Listener(*wlr.Pointer.event.SwipeEnd).init(handleSwipeEnd),
|
||||||
|
|
||||||
|
touch_up: wl.Listener(*wlr.Touch.event.Up) =
|
||||||
|
wl.Listener(*wlr.Touch.event.Up).init(handleTouchUp),
|
||||||
|
touch_down: wl.Listener(*wlr.Touch.event.Down) =
|
||||||
|
wl.Listener(*wlr.Touch.event.Down).init(handleTouchDown),
|
||||||
|
touch_motion: wl.Listener(*wlr.Touch.event.Motion) =
|
||||||
|
wl.Listener(*wlr.Touch.event.Motion).init(handleTouchMotion),
|
||||||
|
touch_frame: wl.Listener(void) = wl.Listener(void).init(handleTouchFrame),
|
||||||
|
|
||||||
pub fn init(self: *Self, seat: *Seat) !void {
|
pub fn init(self: *Self, seat: *Seat) !void {
|
||||||
const wlr_cursor = try wlr.Cursor.create();
|
const wlr_cursor = try wlr.Cursor.create();
|
||||||
errdefer wlr_cursor.destroy();
|
errdefer wlr_cursor.destroy();
|
||||||
@ -170,6 +178,12 @@ pub fn init(self: *Self, seat: *Seat) !void {
|
|||||||
wlr_cursor.events.pinch_update.add(&self.pinch_update);
|
wlr_cursor.events.pinch_update.add(&self.pinch_update);
|
||||||
wlr_cursor.events.pinch_end.add(&self.pinch_end);
|
wlr_cursor.events.pinch_end.add(&self.pinch_end);
|
||||||
seat.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor);
|
seat.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor);
|
||||||
|
|
||||||
|
// TODO(wlroots) handle the cancel event, blocked on wlroots 0.16.0
|
||||||
|
wlr_cursor.events.touch_up.add(&self.touch_up);
|
||||||
|
wlr_cursor.events.touch_down.add(&self.touch_down);
|
||||||
|
wlr_cursor.events.touch_motion.add(&self.touch_motion);
|
||||||
|
wlr_cursor.events.touch_frame.add(&self.touch_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
@ -297,11 +311,33 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (self.surfaceAt()) |result| {
|
if (self.surfaceAt()) |result| {
|
||||||
|
if (result.parent == .view and self.handlePointerMapping(event, result.parent.view)) {
|
||||||
|
// If a mapping is triggered don't send events to clients.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateKeyboardFocus(result);
|
||||||
|
|
||||||
|
_ = 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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
self.updateOutputFocus(self.wlr_cursor.x, self.wlr_cursor.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.root.startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void {
|
||||||
switch (result.parent) {
|
switch (result.parent) {
|
||||||
.view => |view| {
|
.view => |view| {
|
||||||
// If there is an active mapping for this button which is
|
|
||||||
// handled we are done here
|
|
||||||
if (self.handlePointerMapping(event, view)) return;
|
|
||||||
// Otherwise focus the view
|
// Otherwise focus the view
|
||||||
self.seat.focus(view);
|
self.seat.focus(view);
|
||||||
},
|
},
|
||||||
@ -324,24 +360,14 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
}
|
||||||
|
|
||||||
self.mode = .{
|
/// Focus the output at the given layout coordinates, if any
|
||||||
.down = .{
|
fn updateOutputFocus(self: Self, lx: f64, ly: f64) void {
|
||||||
.lx = self.wlr_cursor.x,
|
if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| {
|
||||||
.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| {
|
|
||||||
// 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);
|
||||||
self.seat.focusOutput(output);
|
self.seat.focusOutput(output);
|
||||||
self.seat.focus(null);
|
self.seat.focus(null);
|
||||||
server.root.startTransaction();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,6 +447,71 @@ fn handleSwipeEnd(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handleTouchUp(
|
||||||
|
listener: *wl.Listener(*wlr.Touch.event.Up),
|
||||||
|
event: *wlr.Touch.event.Up,
|
||||||
|
) void {
|
||||||
|
const self = @fieldParentPtr(Self, "touch_up", listener);
|
||||||
|
|
||||||
|
self.seat.handleActivity();
|
||||||
|
|
||||||
|
self.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleTouchDown(
|
||||||
|
listener: *wl.Listener(*wlr.Touch.event.Down),
|
||||||
|
event: *wlr.Touch.event.Down,
|
||||||
|
) void {
|
||||||
|
const self = @fieldParentPtr(Self, "touch_down", listener);
|
||||||
|
|
||||||
|
self.seat.handleActivity();
|
||||||
|
|
||||||
|
var lx: f64 = undefined;
|
||||||
|
var ly: f64 = undefined;
|
||||||
|
self.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &lx, &ly);
|
||||||
|
|
||||||
|
if (surfaceAtCoords(lx, ly)) |result| {
|
||||||
|
self.updateKeyboardFocus(result);
|
||||||
|
|
||||||
|
_ = self.seat.wlr_seat.touchNotifyDown(
|
||||||
|
result.surface,
|
||||||
|
event.time_msec,
|
||||||
|
event.touch_id,
|
||||||
|
result.sx,
|
||||||
|
result.sy,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.updateOutputFocus(lx, ly);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.root.startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleTouchMotion(
|
||||||
|
listener: *wl.Listener(*wlr.Touch.event.Motion),
|
||||||
|
event: *wlr.Touch.event.Motion,
|
||||||
|
) void {
|
||||||
|
const self = @fieldParentPtr(Self, "touch_motion", listener);
|
||||||
|
|
||||||
|
self.seat.handleActivity();
|
||||||
|
|
||||||
|
var lx: f64 = undefined;
|
||||||
|
var ly: f64 = undefined;
|
||||||
|
self.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &lx, &ly);
|
||||||
|
|
||||||
|
if (surfaceAtCoords(lx, ly)) |result| {
|
||||||
|
self.seat.wlr_seat.touchNotifyMotion(event.time_msec, event.touch_id, result.sx, result.sy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleTouchFrame(listener: *wl.Listener(void)) void {
|
||||||
|
const self = @fieldParentPtr(Self, "touch_frame", listener);
|
||||||
|
|
||||||
|
self.seat.handleActivity();
|
||||||
|
|
||||||
|
self.seat.wlr_seat.touchNotifyFrame();
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle the mapping for the passed button if any. Returns true if there
|
/// Handle the mapping for the passed button if any. Returns true if there
|
||||||
/// was a mapping and the button was handled.
|
/// was a mapping and the button was handled.
|
||||||
fn handlePointerMapping(self: *Self, event: *wlr.Pointer.event.Button, view: *View) bool {
|
fn handlePointerMapping(self: *Self, event: *wlr.Pointer.event.Button, view: *View) bool {
|
||||||
@ -544,10 +635,14 @@ const SurfaceAtResult = struct {
|
|||||||
|
|
||||||
/// Find the surface under the cursor if any, and return information about that
|
/// Find the surface under the cursor if any, and return information about that
|
||||||
/// surface and the cursor's position in surface local coords.
|
/// surface and the cursor's position in surface local coords.
|
||||||
/// This function must be kept in sync with the rendering order in render.zig.
|
|
||||||
pub fn surfaceAt(self: Self) ?SurfaceAtResult {
|
pub fn surfaceAt(self: Self) ?SurfaceAtResult {
|
||||||
const lx = self.wlr_cursor.x;
|
return surfaceAtCoords(self.wlr_cursor.x, self.wlr_cursor.y);
|
||||||
const ly = self.wlr_cursor.y;
|
}
|
||||||
|
|
||||||
|
/// Find the surface at the given layout coords if any, and return information about that
|
||||||
|
/// surface and the surface local coords.
|
||||||
|
/// This function must be kept in sync with the rendering order in render.zig.
|
||||||
|
fn surfaceAtCoords(lx: f64, ly: f64) ?SurfaceAtResult {
|
||||||
const wlr_output = server.root.output_layout.outputAt(lx, ly) orelse return null;
|
const wlr_output = server.root.output_layout.outputAt(lx, ly) orelse return null;
|
||||||
const output = @intToPtr(*Output, wlr_output.data);
|
const output = @intToPtr(*Output, wlr_output.data);
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice)
|
|||||||
keyboard.deinit();
|
keyboard.deinit();
|
||||||
util.gpa.destroy(keyboard);
|
util.gpa.destroy(keyboard);
|
||||||
},
|
},
|
||||||
.pointer => {
|
.pointer, .touch => {
|
||||||
device.deinit();
|
device.deinit();
|
||||||
util.gpa.destroy(device);
|
util.gpa.destroy(device);
|
||||||
},
|
},
|
||||||
@ -121,6 +121,6 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice)
|
|||||||
switch_device.deinit();
|
switch_device.deinit();
|
||||||
util.gpa.destroy(switch_device);
|
util.gpa.destroy(switch_device);
|
||||||
},
|
},
|
||||||
.touch, .tablet_tool, .tablet_pad => unreachable,
|
.tablet_tool, .tablet_pad => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ fn tryAddDevice(self: *Self, wlr_device: *wlr.InputDevice) !void {
|
|||||||
|
|
||||||
try keyboard.init(self, wlr_device);
|
try keyboard.init(self, wlr_device);
|
||||||
},
|
},
|
||||||
.pointer => {
|
.pointer, .touch => {
|
||||||
const device = try util.gpa.create(InputDevice);
|
const device = try util.gpa.create(InputDevice);
|
||||||
errdefer util.gpa.destroy(device);
|
errdefer util.gpa.destroy(device);
|
||||||
|
|
||||||
@ -487,7 +487,7 @@ fn tryAddDevice(self: *Self, wlr_device: *wlr.InputDevice) !void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// TODO Support these types of input devices.
|
// TODO Support these types of input devices.
|
||||||
.touch, .tablet_tool, .tablet_pad => return,
|
.tablet_tool, .tablet_pad => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,8 +501,9 @@ pub fn updateCapabilities(self: *Self) void {
|
|||||||
if (device.seat == self) {
|
if (device.seat == self) {
|
||||||
switch (device.wlr_device.type) {
|
switch (device.wlr_device.type) {
|
||||||
.keyboard => capabilities.keyboard = true,
|
.keyboard => capabilities.keyboard = true,
|
||||||
|
.touch => capabilities.touch = true,
|
||||||
.pointer, .switch_device => {},
|
.pointer, .switch_device => {},
|
||||||
.touch, .tablet_tool, .tablet_pad => unreachable,
|
.tablet_tool, .tablet_pad => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user