diff --git a/river/Cursor.zig b/river/Cursor.zig index f66d2be..49ae33c 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -129,6 +129,14 @@ swipe_update: wl.Listener(*wlr.Pointer.event.SwipeUpdate) = swipe_end: wl.Listener(*wlr.Pointer.event.SwipeEnd) = 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 { const wlr_cursor = try wlr.Cursor.create(); 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_end.add(&self.pinch_end); 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 { @@ -297,33 +311,13 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P } if (self.surfaceAt()) |result| { - switch (result.parent) { - .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 - self.seat.focus(view); - }, - .layer_surface => |layer_surface| { - self.seat.focusOutput(layer_surface.output); - // If a keyboard inteactive layer surface has been clicked on, - // give it keyboard focus. - if (layer_surface.wlr_layer_surface.current.keyboard_interactive == .exclusive) { - self.seat.setFocusRaw(.{ .layer = layer_surface }); - } else { - self.seat.focus(null); - } - }, - .xwayland_override_redirect => |override_redirect| { - if (!build_options.xwayland) unreachable; - if (override_redirect.xwayland_surface.overrideRedirectWantsFocus() and - override_redirect.xwayland_surface.icccmInputModel() != .none) - { - self.seat.setFocusRaw(.{ .xwayland_override_redirect = override_redirect }); - } - }, + 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 = .{ @@ -334,14 +328,46 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P .sy = result.sy, }, }; + } else { + self.updateOutputFocus(self.wlr_cursor.x, self.wlr_cursor.y); + } - 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. + server.root.startTransaction(); +} + +fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void { + switch (result.parent) { + .view => |view| { + // Otherwise focus the view + self.seat.focus(view); + }, + .layer_surface => |layer_surface| { + self.seat.focusOutput(layer_surface.output); + // If a keyboard inteactive layer surface has been clicked on, + // give it keyboard focus. + if (layer_surface.wlr_layer_surface.current.keyboard_interactive == .exclusive) { + self.seat.setFocusRaw(.{ .layer = layer_surface }); + } else { + self.seat.focus(null); + } + }, + .xwayland_override_redirect => |override_redirect| { + if (!build_options.xwayland) unreachable; + if (override_redirect.xwayland_surface.overrideRedirectWantsFocus() and + override_redirect.xwayland_surface.icccmInputModel() != .none) + { + self.seat.setFocusRaw(.{ .xwayland_override_redirect = override_redirect }); + } + }, + } +} + +/// Focus the output at the given layout coordinates, if any +fn updateOutputFocus(self: Self, lx: f64, ly: f64) void { + if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| { const output = @intToPtr(*Output, wlr_output.data); self.seat.focusOutput(output); 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 /// was a mapping and the button was handled. 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 /// 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 { - const lx = self.wlr_cursor.x; - const ly = self.wlr_cursor.y; + return surfaceAtCoords(self.wlr_cursor.x, 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 output = @intToPtr(*Output, wlr_output.data); diff --git a/river/InputDevice.zig b/river/InputDevice.zig index 69cd3da..f3ddcb2 100644 --- a/river/InputDevice.zig +++ b/river/InputDevice.zig @@ -112,7 +112,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) keyboard.deinit(); util.gpa.destroy(keyboard); }, - .pointer => { + .pointer, .touch => { device.deinit(); util.gpa.destroy(device); }, @@ -121,6 +121,6 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) switch_device.deinit(); util.gpa.destroy(switch_device); }, - .touch, .tablet_tool, .tablet_pad => unreachable, + .tablet_tool, .tablet_pad => unreachable, } } diff --git a/river/Seat.zig b/river/Seat.zig index c30bce7..c04e2eb 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -471,7 +471,7 @@ fn tryAddDevice(self: *Self, wlr_device: *wlr.InputDevice) !void { try keyboard.init(self, wlr_device); }, - .pointer => { + .pointer, .touch => { const device = try util.gpa.create(InputDevice); 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. - .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) { switch (device.wlr_device.type) { .keyboard => capabilities.keyboard = true, + .touch => capabilities.touch = true, .pointer, .switch_device => {}, - .touch, .tablet_tool, .tablet_pad => unreachable, + .tablet_tool, .tablet_pad => unreachable, } } }