river: pointer-constraints and relative-pointer
This commit is contained in:
parent
4beb39920a
commit
26b0acddb7
1
AUTHORS
1
AUTHORS
@ -3,3 +3,4 @@ The following developers have contributed code to river:
|
||||
Leon Henrik Plickat
|
||||
Marten Ringwelski
|
||||
Rishabh Das
|
||||
Bonicgamer
|
||||
|
@ -42,6 +42,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||
scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
|
||||
scanner.addSystemProtocol("unstable/xdg-output/xdg-output-unstable-v1.xml");
|
||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
||||
scanner.addProtocolPath("protocol/river-options-unstable-v1.xml");
|
||||
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
|
||||
|
2
deps/zig-wlroots
vendored
2
deps/zig-wlroots
vendored
@ -1 +1 @@
|
||||
Subproject commit b38d3d5d2d9a5e4c748b8c01ed0d3861241661a6
|
||||
Subproject commit 1bdbb7a15a4038ff8bf7c9272c6a4d6eeb64ffa2
|
@ -60,6 +60,8 @@ wlr_cursor: *wlr.Cursor,
|
||||
pointer_gestures: *wlr.PointerGesturesV1,
|
||||
xcursor_manager: *wlr.XcursorManager,
|
||||
|
||||
constraint: ?*wlr.PointerConstraintV1 = null,
|
||||
|
||||
/// Number of distinct buttons currently pressed
|
||||
pressed_count: u32 = 0,
|
||||
|
||||
@ -382,7 +384,9 @@ fn handleMotionAbsolute(
|
||||
var ly: f64 = undefined;
|
||||
self.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &lx, &ly);
|
||||
|
||||
self.processMotion(event.device, event.time_msec, lx - self.wlr_cursor.x, ly - self.wlr_cursor.y);
|
||||
const dx = lx - self.wlr_cursor.x;
|
||||
const dy = ly - self.wlr_cursor.y;
|
||||
self.processMotion(event.device, event.time_msec, dx, dy, dx, dy);
|
||||
}
|
||||
|
||||
/// This event is forwarded by the cursor when a pointer emits a _relative_
|
||||
@ -395,7 +399,7 @@ fn handleMotion(
|
||||
|
||||
self.seat.handleActivity();
|
||||
|
||||
self.processMotion(event.device, event.time_msec, event.delta_x, event.delta_y);
|
||||
self.processMotion(event.device, event.time_msec, event.delta_x, event.delta_y, event.unaccel_dx, event.unaccel_dy);
|
||||
}
|
||||
|
||||
fn handleRequestSetCursor(
|
||||
@ -561,14 +565,44 @@ fn leaveMode(self: *Self, event: *wlr.Pointer.event.Button) void {
|
||||
self.passthrough(event.time_msec);
|
||||
}
|
||||
|
||||
fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, delta_y: 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.seat.input_manager.relative_pointer_manager.sendRelativeMotion(
|
||||
self.seat.wlr_seat,
|
||||
@as(u64, time) * 1000,
|
||||
delta_x,
|
||||
delta_y,
|
||||
unaccel_dx,
|
||||
unaccel_dy,
|
||||
);
|
||||
var dx: f64 = delta_x;
|
||||
var dy: f64 = delta_y;
|
||||
if (self.constraint) |constraint| {
|
||||
if (self.mode == .passthrough or self.mode == .down) {
|
||||
if (constraint.type == .locked) return;
|
||||
|
||||
var sx: f64 = undefined;
|
||||
var sy: f64 = undefined;
|
||||
const surface = self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy);
|
||||
|
||||
if (surface != constraint.surface) return;
|
||||
|
||||
var sx_con: f64 = undefined;
|
||||
var sy_con: f64 = undefined;
|
||||
if (!wlr.region.confine(&constraint.region, sx, sy, sx + dx, sy + dy, &sx_con, &sy_con)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dx = sx_con - sx;
|
||||
dy = sy_con - sy;
|
||||
}
|
||||
}
|
||||
switch (self.mode) {
|
||||
.passthrough => {
|
||||
self.wlr_cursor.move(device, delta_x, delta_y);
|
||||
self.wlr_cursor.move(device, dx, dy);
|
||||
self.passthrough(time);
|
||||
},
|
||||
.down => |view| {
|
||||
self.wlr_cursor.move(device, delta_x, delta_y);
|
||||
self.wlr_cursor.move(device, dx, dy);
|
||||
// This takes surface-local coordinates
|
||||
const output_box = view.output.root.output_layout.getBox(view.output.wlr_output).?;
|
||||
self.seat.wlr_seat.pointerNotifyMotion(
|
||||
|
@ -27,6 +27,7 @@ const util = @import("util.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
const Server = @import("Server.zig");
|
||||
const View = @import("View.zig");
|
||||
const PointerConstraint = @import("PointerConstraint.zig");
|
||||
|
||||
const default_seat_name = "default";
|
||||
|
||||
@ -37,6 +38,8 @@ new_input: wl.Listener(*wlr.InputDevice) = wl.Listener(*wlr.InputDevice).init(ha
|
||||
|
||||
idle: *wlr.Idle,
|
||||
input_inhibit_manager: *wlr.InputInhibitManager,
|
||||
pointer_constraints: *wlr.PointerConstraintsV1,
|
||||
relative_pointer_manager: *wlr.RelativePointerManagerV1,
|
||||
virtual_pointer_manager: *wlr.VirtualPointerManagerV1,
|
||||
virtual_keyboard_manager: *wlr.VirtualKeyboardManagerV1,
|
||||
|
||||
@ -49,6 +52,8 @@ inhibit_activate: wl.Listener(*wlr.InputInhibitManager) =
|
||||
wl.Listener(*wlr.InputInhibitManager).init(handleInhibitActivate),
|
||||
inhibit_deactivate: wl.Listener(*wlr.InputInhibitManager) =
|
||||
wl.Listener(*wlr.InputInhibitManager).init(handleInhibitDeactivate),
|
||||
new_pointer_constraint: wl.Listener(*wlr.PointerConstraintV1) =
|
||||
wl.Listener(*wlr.PointerConstraintV1).init(handleNewPointerConstraint),
|
||||
new_virtual_pointer: wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer) =
|
||||
wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer).init(handleNewVirtualPointer),
|
||||
new_virtual_keyboard: wl.Listener(*wlr.VirtualKeyboardV1) =
|
||||
@ -64,6 +69,8 @@ pub fn init(self: *Self, server: *Server) !void {
|
||||
// These are automatically freed when the display is destroyed
|
||||
.idle = try wlr.Idle.create(server.wl_server),
|
||||
.input_inhibit_manager = try wlr.InputInhibitManager.create(server.wl_server),
|
||||
.pointer_constraints = try wlr.PointerConstraintsV1.create(server.wl_server),
|
||||
.relative_pointer_manager = try wlr.RelativePointerManagerV1.create(server.wl_server),
|
||||
.virtual_pointer_manager = try wlr.VirtualPointerManagerV1.create(server.wl_server),
|
||||
.virtual_keyboard_manager = try wlr.VirtualKeyboardManagerV1.create(server.wl_server),
|
||||
};
|
||||
@ -76,6 +83,7 @@ pub fn init(self: *Self, server: *Server) !void {
|
||||
server.backend.events.new_input.add(&self.new_input);
|
||||
self.input_inhibit_manager.events.activate.add(&self.inhibit_activate);
|
||||
self.input_inhibit_manager.events.deactivate.add(&self.inhibit_deactivate);
|
||||
self.pointer_constraints.events.new_constraint.add(&self.new_pointer_constraint);
|
||||
self.virtual_pointer_manager.events.new_virtual_pointer.add(&self.new_virtual_pointer);
|
||||
self.virtual_keyboard_manager.events.new_virtual_keyboard.add(&self.new_virtual_keyboard);
|
||||
}
|
||||
@ -171,6 +179,15 @@ fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDe
|
||||
self.defaultSeat().addDevice(device);
|
||||
}
|
||||
|
||||
fn handleNewPointerConstraint(listener: *wl.Listener(*wlr.PointerConstraintV1), constraint: *wlr.PointerConstraintV1) void {
|
||||
const pointer_constraint = util.gpa.create(PointerConstraint) catch {
|
||||
log.crit("out of memory", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
pointer_constraint.init(constraint);
|
||||
}
|
||||
|
||||
fn handleNewVirtualPointer(
|
||||
listener: *wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer),
|
||||
event: *wlr.VirtualPointerManagerV1.event.NewPointer,
|
||||
|
132
river/PointerConstraint.zig
Normal file
132
river/PointerConstraint.zig
Normal file
@ -0,0 +1,132 @@
|
||||
// This file is part of river, a dynamic tiling wayland compositor.
|
||||
//
|
||||
// Copyright 2021 The River Developers
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const build_options = @import("build_options");
|
||||
const std = @import("std");
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
const pixman = @import("pixman");
|
||||
|
||||
const log = @import("log.zig");
|
||||
const util = @import("util.zig");
|
||||
|
||||
const Cursor = @import("Cursor.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
const View = @import("View.zig");
|
||||
|
||||
constraint: *wlr.PointerConstraintV1,
|
||||
cursor: *Cursor,
|
||||
|
||||
// zig fmt: off
|
||||
destroy: wl.Listener(*wlr.PointerConstraintV1) =
|
||||
wl.Listener(*wlr.PointerConstraintV1).init(handleDestroy),
|
||||
set_region: wl.Listener(void) =
|
||||
wl.Listener(void).init(handleSetRegion),
|
||||
// zig fmt: on
|
||||
|
||||
pub fn init(self: *Self, constraint: *wlr.PointerConstraintV1) void {
|
||||
const seat = @intToPtr(*Seat, constraint.seat.data);
|
||||
self.* = .{
|
||||
.constraint = constraint,
|
||||
.cursor = &seat.cursor,
|
||||
};
|
||||
|
||||
self.constraint.data = @ptrToInt(self);
|
||||
|
||||
self.constraint.events.destroy.add(&self.destroy);
|
||||
self.constraint.events.set_region.add(&self.set_region);
|
||||
|
||||
if (seat.focused == .view and seat.focused.view.surface == self.constraint.surface) {
|
||||
self.setAsActive();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setAsActive(self: *Self) void {
|
||||
if (self.cursor.constraint == self.constraint) return;
|
||||
|
||||
if (self.cursor.constraint) |constraint| {
|
||||
constraint.sendDeactivated();
|
||||
}
|
||||
|
||||
self.cursor.constraint = self.constraint;
|
||||
|
||||
if (self.constraint.current.region.notEmpty() != 0) {
|
||||
_ = self.constraint.region.intersect(&self.constraint.surface.input_region, &self.constraint.current.region);
|
||||
} else {
|
||||
_ = self.constraint.region.copy(&self.constraint.surface.input_region);
|
||||
}
|
||||
self.constrainToRegion();
|
||||
|
||||
self.constraint.sendActivated();
|
||||
}
|
||||
|
||||
fn constrainToRegion(self: *Self) void {
|
||||
if (self.cursor.constraint != self.constraint) return;
|
||||
if (View.fromWlrSurface(self.constraint.surface)) |view| {
|
||||
const cx = @floatToInt(c_int, self.cursor.wlr_cursor.x) - @intCast(c_int, view.current.box.x);
|
||||
const cy = @floatToInt(c_int, self.cursor.wlr_cursor.y) - @intCast(c_int, view.current.box.y);
|
||||
|
||||
var box: pixman.Box32 = undefined;
|
||||
|
||||
if (self.constraint.region.containsPoint(cx, cy, &box) == 0) {
|
||||
var nRects: c_int = undefined;
|
||||
const rects = self.constraint.region.rectangles(&nRects);
|
||||
|
||||
if (nRects > 0) {
|
||||
const new_cx = @intToFloat(f64, view.current.box.x + rects.x1 + @divFloor(rects.x2, 2));
|
||||
const new_cy = @intToFloat(f64, view.current.box.y + rects.y1 + @divFloor(rects.y2, 2));
|
||||
|
||||
self.cursor.wlr_cursor.warpClosest(null, new_cx, new_cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), constraint: *wlr.PointerConstraintV1) void {
|
||||
const self = @fieldParentPtr(Self, "destroy", listener);
|
||||
|
||||
self.destroy.link.remove();
|
||||
self.set_region.link.remove();
|
||||
|
||||
if (self.cursor.constraint == self.constraint) {
|
||||
warpToHint(self.cursor);
|
||||
|
||||
self.cursor.constraint = null;
|
||||
}
|
||||
|
||||
util.gpa.destroy(self);
|
||||
}
|
||||
|
||||
fn handleSetRegion(listener: *wl.Listener(void)) void {
|
||||
const self = @fieldParentPtr(Self, "set_region", listener);
|
||||
self.constrainToRegion();
|
||||
}
|
||||
|
||||
pub fn warpToHint(cursor: *Cursor) void {
|
||||
if (cursor.constraint) |constraint| {
|
||||
if (constraint.current.committed.cursor_hint) {
|
||||
if (View.fromWlrSurface(constraint.surface)) |view| {
|
||||
const cx = constraint.current.cursor_hint.x + @intToFloat(f64, view.current.box.x);
|
||||
const cy = constraint.current.cursor_hint.y + @intToFloat(f64, view.current.box.y);
|
||||
|
||||
_ = cursor.wlr_cursor.warp(null, cx, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ const View = @import("View.zig");
|
||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||
|
||||
const log = std.log.scoped(.seat);
|
||||
const PointerConstraint = @import("PointerConstraint.zig");
|
||||
|
||||
const FocusTarget = union(enum) {
|
||||
view: *View,
|
||||
@ -223,7 +224,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
}
|
||||
self.focused = new_focus;
|
||||
|
||||
// Send surface enter/leave events
|
||||
// Send keyboard enter/leave events and handle pointer constraints
|
||||
if (target_surface) |wlr_surface| {
|
||||
if (self.wlr_seat.getKeyboard()) |keyboard| {
|
||||
self.wlr_seat.keyboardNotifyEnter(
|
||||
@ -235,8 +236,22 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
} else {
|
||||
self.wlr_seat.keyboardNotifyEnter(wlr_surface, null, 0, null);
|
||||
}
|
||||
|
||||
if (self.input_manager.pointer_constraints.constraintForSurface(wlr_surface, self.wlr_seat)) |constraint| {
|
||||
@intToPtr(*PointerConstraint, constraint.data).setAsActive();
|
||||
} else if (self.cursor.constraint) |constraint| {
|
||||
PointerConstraint.warpToHint(&self.cursor);
|
||||
constraint.sendDeactivated();
|
||||
self.cursor.constraint = null;
|
||||
}
|
||||
} else {
|
||||
self.wlr_seat.keyboardClearFocus();
|
||||
|
||||
if (self.cursor.constraint) |constraint| {
|
||||
PointerConstraint.warpToHint(&self.cursor);
|
||||
constraint.sendDeactivated();
|
||||
self.cursor.constraint = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user