pointer-constraints: implement protocol
Now with 50% less pointer warping! The new implementation requires the user to move the cursor into the constraint region before the constraint is activated in order to keep behavior more predictable.
This commit is contained in:
parent
1602b34f4f
commit
b7b371cb4f
@ -108,6 +108,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||
|
||||
scanner.generate("xdg_wm_base", 2);
|
||||
scanner.generate("zwp_pointer_gestures_v1", 3);
|
||||
scanner.generate("zwp_pointer_constraints_v1", 1);
|
||||
scanner.generate("ext_session_lock_manager_v1", 1);
|
||||
|
||||
scanner.generate("zriver_control_v1", 1);
|
||||
|
@ -35,6 +35,7 @@ const DragIcon = @import("DragIcon.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const PointerConstraint = @import("PointerConstraint.zig");
|
||||
const Root = @import("Root.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
const View = @import("View.zig");
|
||||
@ -145,6 +146,11 @@ hide_cursor_timer: *wl.EventSource,
|
||||
hidden: bool = false,
|
||||
may_need_warp: bool = false,
|
||||
|
||||
/// The pointer constraint for the surface that currently has keyboard focus, if any.
|
||||
/// This constraint is not necessarily active, activation only occurs once the cursor
|
||||
/// has been moved inside the constraint region.
|
||||
constraint: ?*PointerConstraint = null,
|
||||
|
||||
last_focus_follows_cursor_target: ?*View = null,
|
||||
|
||||
/// Keeps track of the last known location of all touch points in layout coordinates.
|
||||
@ -344,7 +350,7 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
||||
}
|
||||
|
||||
if (server.root.at(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
|
||||
if (result.node == .view and self.handlePointerMapping(event, result.node.view)) {
|
||||
if (result.data == .view and self.handlePointerMapping(event, result.data.view)) {
|
||||
// If a mapping is triggered don't send events to clients.
|
||||
return;
|
||||
}
|
||||
@ -372,7 +378,7 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
||||
|
||||
/// Requires a call to Root.applyPending()
|
||||
fn updateKeyboardFocus(self: Self, result: Root.AtResult) void {
|
||||
switch (result.node) {
|
||||
switch (result.data) {
|
||||
.view => |view| {
|
||||
self.seat.focus(view);
|
||||
},
|
||||
@ -678,6 +684,10 @@ fn handleHideCursorTimeout(self: *Self) c_int {
|
||||
}
|
||||
|
||||
pub fn startMove(cursor: *Self, view: *View) void {
|
||||
if (cursor.constraint) |constraint| {
|
||||
if (constraint.state == .active) constraint.deactivate();
|
||||
}
|
||||
|
||||
const new_mode: Mode = .{ .move = .{
|
||||
.view = view,
|
||||
.offset_x = @floatToInt(i32, cursor.wlr_cursor.x) - view.current.box.x,
|
||||
@ -687,6 +697,10 @@ pub fn startMove(cursor: *Self, view: *View) void {
|
||||
}
|
||||
|
||||
pub fn startResize(cursor: *Self, view: *View, proposed_edges: ?wlr.Edges) void {
|
||||
if (cursor.constraint) |constraint| {
|
||||
if (constraint.state == .active) constraint.deactivate();
|
||||
}
|
||||
|
||||
const edges = blk: {
|
||||
if (proposed_edges) |edges| {
|
||||
if (edges.top or edges.bottom or edges.left or edges.right) {
|
||||
@ -803,21 +817,40 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
||||
|
||||
var dx: f64 = delta_x;
|
||||
var dy: f64 = delta_y;
|
||||
|
||||
if (self.constraint) |constraint| {
|
||||
if (constraint.state == .active) {
|
||||
switch (constraint.wlr_constraint.type) {
|
||||
.locked => return,
|
||||
.confined => constraint.confine(&dx, &dy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (self.mode) {
|
||||
.passthrough => {
|
||||
.passthrough, .down => {
|
||||
self.wlr_cursor.move(device, dx, dy);
|
||||
self.checkFocusFollowsCursor();
|
||||
self.passthrough(time);
|
||||
self.updateDragIcons();
|
||||
},
|
||||
.down => |down| {
|
||||
self.wlr_cursor.move(device, dx, dy);
|
||||
self.seat.wlr_seat.pointerNotifyMotion(
|
||||
time,
|
||||
down.sx + (self.wlr_cursor.x - down.lx),
|
||||
down.sy + (self.wlr_cursor.y - down.ly),
|
||||
);
|
||||
|
||||
switch (self.mode) {
|
||||
.passthrough => {
|
||||
self.checkFocusFollowsCursor();
|
||||
self.passthrough(time);
|
||||
},
|
||||
.down => |data| {
|
||||
self.seat.wlr_seat.pointerNotifyMotion(
|
||||
time,
|
||||
data.sx + (self.wlr_cursor.x - data.lx),
|
||||
data.sy + (self.wlr_cursor.y - data.ly),
|
||||
);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
self.updateDragIcons();
|
||||
|
||||
if (self.constraint) |constraint| {
|
||||
constraint.maybeActivate();
|
||||
}
|
||||
},
|
||||
.move => |*data| {
|
||||
dx += data.delta_x;
|
||||
@ -904,7 +937,7 @@ pub fn checkFocusFollowsCursor(self: *Self) void {
|
||||
if (self.seat.drag == .pointer) return;
|
||||
if (server.config.focus_follows_cursor == .disabled) return;
|
||||
if (server.root.at(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
|
||||
switch (result.node) {
|
||||
switch (result.data) {
|
||||
.view => |view| {
|
||||
// Don't re-focus the last focused view when the mode is .normal
|
||||
if (server.config.focus_follows_cursor == .normal and
|
||||
@ -941,6 +974,11 @@ pub fn updateState(self: *Self) void {
|
||||
if (self.may_need_warp) {
|
||||
self.warp();
|
||||
}
|
||||
|
||||
if (self.constraint) |constraint| {
|
||||
constraint.updateState();
|
||||
}
|
||||
|
||||
if (self.shouldPassthrough()) {
|
||||
self.mode = .passthrough;
|
||||
var now: os.timespec = undefined;
|
||||
@ -986,7 +1024,7 @@ fn passthrough(self: *Self, time: u32) void {
|
||||
assert(self.mode == .passthrough);
|
||||
|
||||
if (server.root.at(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
|
||||
if (result.node == .lock_surface) {
|
||||
if (result.data == .lock_surface) {
|
||||
assert(server.lock_manager.state != .unlocked);
|
||||
} else {
|
||||
assert(server.lock_manager.state != .locked);
|
||||
|
@ -29,6 +29,7 @@ const util = @import("util.zig");
|
||||
const InputConfig = @import("InputConfig.zig");
|
||||
const InputDevice = @import("InputDevice.zig");
|
||||
const Keyboard = @import("Keyboard.zig");
|
||||
const PointerConstraint = @import("PointerConstraint.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
const Switch = @import("Switch.zig");
|
||||
|
||||
@ -42,6 +43,7 @@ idle_notifier: *wlr.IdleNotifierV1,
|
||||
relative_pointer_manager: *wlr.RelativePointerManagerV1,
|
||||
virtual_pointer_manager: *wlr.VirtualPointerManagerV1,
|
||||
virtual_keyboard_manager: *wlr.VirtualKeyboardManagerV1,
|
||||
pointer_constraints: *wlr.PointerConstraintsV1,
|
||||
|
||||
configs: std.ArrayList(InputConfig),
|
||||
devices: wl.list.Head(InputDevice, .link),
|
||||
@ -53,6 +55,8 @@ new_virtual_pointer: wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer)
|
||||
wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer).init(handleNewVirtualPointer),
|
||||
new_virtual_keyboard: wl.Listener(*wlr.VirtualKeyboardV1) =
|
||||
wl.Listener(*wlr.VirtualKeyboardV1).init(handleNewVirtualKeyboard),
|
||||
new_constraint: wl.Listener(*wlr.PointerConstraintV1) =
|
||||
wl.Listener(*wlr.PointerConstraintV1).init(handleNewConstraint),
|
||||
|
||||
pub fn init(self: *Self) !void {
|
||||
const seat_node = try util.gpa.create(std.TailQueue(Seat).Node);
|
||||
@ -64,6 +68,7 @@ pub fn init(self: *Self) !void {
|
||||
.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),
|
||||
.pointer_constraints = try wlr.PointerConstraintsV1.create(server.wl_server),
|
||||
.configs = std.ArrayList(InputConfig).init(util.gpa),
|
||||
|
||||
.devices = undefined,
|
||||
@ -78,12 +83,17 @@ pub fn init(self: *Self) !void {
|
||||
server.backend.events.new_input.add(&self.new_input);
|
||||
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);
|
||||
self.pointer_constraints.events.new_constraint.add(&self.new_constraint);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
// This function must be called after the backend has been destroyed
|
||||
assert(self.devices.empty());
|
||||
|
||||
self.new_virtual_pointer.link.remove();
|
||||
self.new_virtual_keyboard.link.remove();
|
||||
self.new_constraint.link.remove();
|
||||
|
||||
while (self.seats.pop()) |seat_node| {
|
||||
seat_node.data.deinit();
|
||||
util.gpa.destroy(seat_node);
|
||||
@ -138,3 +148,13 @@ fn handleNewVirtualKeyboard(
|
||||
const seat = @intToPtr(*Seat, virtual_keyboard.seat.data);
|
||||
seat.addDevice(&virtual_keyboard.keyboard.base);
|
||||
}
|
||||
|
||||
fn handleNewConstraint(
|
||||
_: *wl.Listener(*wlr.PointerConstraintV1),
|
||||
wlr_constraint: *wlr.PointerConstraintV1,
|
||||
) void {
|
||||
PointerConstraint.create(wlr_constraint) catch {
|
||||
log.err("out of memory", .{});
|
||||
wlr_constraint.resource.postNoMemory();
|
||||
};
|
||||
}
|
||||
|
225
river/PointerConstraint.zig
Normal file
225
river/PointerConstraint.zig
Normal file
@ -0,0 +1,225 @@
|
||||
// This file is part of river, a dynamic tiling wayland compositor.
|
||||
//
|
||||
// Copyright 2023 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, version 3.
|
||||
//
|
||||
// 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 PointerConstraint = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
|
||||
const server = &@import("main.zig").server;
|
||||
const util = @import("util.zig");
|
||||
|
||||
const Seat = @import("Seat.zig");
|
||||
|
||||
const log = std.log.scoped(.pointer_constraint);
|
||||
|
||||
wlr_constraint: *wlr.PointerConstraintV1,
|
||||
|
||||
state: union(enum) {
|
||||
inactive,
|
||||
active: struct {
|
||||
/// Node of the active constraint surface in the scene graph.
|
||||
node: *wlr.SceneNode,
|
||||
/// Coordinates of the pointer on activation in the surface coordinate system.
|
||||
sx: f64,
|
||||
sy: f64,
|
||||
},
|
||||
} = .inactive,
|
||||
|
||||
destroy: wl.Listener(*wlr.PointerConstraintV1) = wl.Listener(*wlr.PointerConstraintV1).init(handleDestroy),
|
||||
set_region: wl.Listener(void) = wl.Listener(void).init(handleSetRegion),
|
||||
|
||||
node_destroy: wl.Listener(void) = wl.Listener(void).init(handleNodeDestroy),
|
||||
|
||||
pub fn create(wlr_constraint: *wlr.PointerConstraintV1) error{OutOfMemory}!void {
|
||||
const seat = @intToPtr(*Seat, wlr_constraint.seat.data);
|
||||
|
||||
const constraint = try util.gpa.create(PointerConstraint);
|
||||
errdefer util.gpa.destroy(constraint);
|
||||
|
||||
constraint.* = .{
|
||||
.wlr_constraint = wlr_constraint,
|
||||
};
|
||||
wlr_constraint.data = @ptrToInt(constraint);
|
||||
|
||||
wlr_constraint.events.destroy.add(&constraint.destroy);
|
||||
wlr_constraint.events.set_region.add(&constraint.set_region);
|
||||
|
||||
if (seat.wlr_seat.keyboard_state.focused_surface) |surface| {
|
||||
if (surface == wlr_constraint.surface) {
|
||||
assert(seat.cursor.constraint == null);
|
||||
seat.cursor.constraint = constraint;
|
||||
constraint.maybeActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybeActivate(constraint: *PointerConstraint) void {
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
assert(seat.cursor.constraint == constraint);
|
||||
assert(seat.wlr_seat.keyboard_state.focused_surface == constraint.wlr_constraint.surface);
|
||||
|
||||
if (constraint.state == .active) return;
|
||||
|
||||
if (seat.cursor.mode == .move or seat.cursor.mode == .resize) return;
|
||||
|
||||
const result = server.root.at(seat.cursor.wlr_cursor.x, seat.cursor.wlr_cursor.y) orelse return;
|
||||
if (result.surface != constraint.wlr_constraint.surface) return;
|
||||
|
||||
const sx = @floatToInt(i32, result.sx);
|
||||
const sy = @floatToInt(i32, result.sy);
|
||||
if (!constraint.wlr_constraint.region.containsPoint(sx, sy, null)) return;
|
||||
|
||||
assert(constraint.state == .inactive);
|
||||
constraint.state = .{
|
||||
.active = .{
|
||||
.node = result.node,
|
||||
.sx = result.sx,
|
||||
.sy = result.sy,
|
||||
},
|
||||
};
|
||||
result.node.events.destroy.add(&constraint.node_destroy);
|
||||
|
||||
log.info("activating pointer constraint", .{});
|
||||
|
||||
constraint.wlr_constraint.sendActivated();
|
||||
}
|
||||
|
||||
/// Called when the cursor position or content in the scene graph changes
|
||||
pub fn updateState(constraint: *PointerConstraint) void {
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
assert(seat.wlr_seat.keyboard_state.focused_surface == constraint.wlr_constraint.surface);
|
||||
|
||||
constraint.maybeActivate();
|
||||
|
||||
if (constraint.state != .active) return;
|
||||
|
||||
var lx: i32 = undefined;
|
||||
var ly: i32 = undefined;
|
||||
if (!constraint.state.active.node.coords(&lx, &ly)) {
|
||||
log.info("deactivating pointer constraint, scene node disabled", .{});
|
||||
constraint.deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
const warp_lx = @intToFloat(f64, lx) + constraint.state.active.sx;
|
||||
const warp_ly = @intToFloat(f64, ly) + constraint.state.active.sy;
|
||||
if (!seat.cursor.wlr_cursor.warp(null, warp_lx, warp_ly)) {
|
||||
log.info("deactivating pointer constraint, could not warp cursor", .{});
|
||||
constraint.deactivate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn confine(constraint: *PointerConstraint, dx: *f64, dy: *f64) void {
|
||||
assert(constraint.state == .active);
|
||||
assert(constraint.wlr_constraint.type == .confined);
|
||||
|
||||
const region = &constraint.wlr_constraint.region;
|
||||
const sx = constraint.state.active.sx;
|
||||
const sy = constraint.state.active.sy;
|
||||
var new_sx: f64 = undefined;
|
||||
var new_sy: f64 = undefined;
|
||||
assert(wlr.region.confine(region, sx, sy, sx + dx.*, sy + dy.*, &new_sx, &new_sy));
|
||||
|
||||
dx.* = new_sx - sx;
|
||||
dy.* = new_sy - sy;
|
||||
|
||||
constraint.state.active.sx = new_sx;
|
||||
constraint.state.active.sy = new_sy;
|
||||
}
|
||||
|
||||
pub fn deactivate(constraint: *PointerConstraint) void {
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
assert(seat.cursor.constraint == constraint);
|
||||
assert(constraint.state == .active);
|
||||
|
||||
constraint.warpToHintIfSet();
|
||||
|
||||
constraint.state = .inactive;
|
||||
constraint.node_destroy.link.remove();
|
||||
constraint.wlr_constraint.sendDeactivated();
|
||||
}
|
||||
|
||||
fn warpToHintIfSet(constraint: *PointerConstraint) void {
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
if (constraint.wlr_constraint.current.committed.cursor_hint) {
|
||||
var lx: i32 = undefined;
|
||||
var ly: i32 = undefined;
|
||||
_ = constraint.state.active.node.coords(&lx, &ly);
|
||||
|
||||
const sx = constraint.wlr_constraint.current.cursor_hint.x;
|
||||
const sy = constraint.wlr_constraint.current.cursor_hint.y;
|
||||
_ = seat.cursor.wlr_cursor.warp(null, @intToFloat(f64, lx) + sx, @intToFloat(f64, ly) + sy);
|
||||
_ = seat.wlr_seat.pointerWarp(sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
fn handleNodeDestroy(listener: *wl.Listener(void)) void {
|
||||
const constraint = @fieldParentPtr(PointerConstraint, "node_destroy", listener);
|
||||
|
||||
log.info("deactivating pointer constraint, scene node destroyed", .{});
|
||||
constraint.deactivate();
|
||||
}
|
||||
|
||||
fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.PointerConstraintV1) void {
|
||||
const constraint = @fieldParentPtr(PointerConstraint, "destroy", listener);
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
if (constraint.state == .active) {
|
||||
// We can't simply call deactivate() here as it calls sendDeactivated(),
|
||||
// which could in the case of a oneshot constraint lifetime recursively
|
||||
// destroy the constraint.
|
||||
constraint.warpToHintIfSet();
|
||||
constraint.node_destroy.link.remove();
|
||||
}
|
||||
|
||||
constraint.destroy.link.remove();
|
||||
constraint.set_region.link.remove();
|
||||
|
||||
if (seat.cursor.constraint == constraint) {
|
||||
seat.cursor.constraint = null;
|
||||
}
|
||||
|
||||
util.gpa.destroy(constraint);
|
||||
}
|
||||
|
||||
fn handleSetRegion(listener: *wl.Listener(void)) void {
|
||||
const constraint = @fieldParentPtr(PointerConstraint, "set_region", listener);
|
||||
const seat = @intToPtr(*Seat, constraint.wlr_constraint.seat.data);
|
||||
|
||||
switch (constraint.state) {
|
||||
.active => |state| {
|
||||
const sx = @floatToInt(i32, state.sx);
|
||||
const sy = @floatToInt(i32, state.sy);
|
||||
if (!constraint.wlr_constraint.region.containsPoint(sx, sy, null)) {
|
||||
log.info("deactivating pointer constraint, region change left pointer outside constraint", .{});
|
||||
constraint.deactivate();
|
||||
}
|
||||
},
|
||||
.inactive => {
|
||||
if (seat.cursor.constraint == constraint) {
|
||||
constraint.maybeActivate();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -170,15 +170,11 @@ pub fn deinit(self: *Self) void {
|
||||
}
|
||||
|
||||
pub const AtResult = struct {
|
||||
node: *wlr.SceneNode,
|
||||
surface: ?*wlr.Surface,
|
||||
sx: f64,
|
||||
sy: f64,
|
||||
node: union(enum) {
|
||||
view: *View,
|
||||
layer_surface: *LayerSurface,
|
||||
lock_surface: *LockSurface,
|
||||
xwayland_override_redirect: if (build_options.xwayland) *XwaylandOverrideRedirect else noreturn,
|
||||
},
|
||||
data: SceneNodeData.Data,
|
||||
};
|
||||
|
||||
/// Return information about what is currently rendered in the interactive_content
|
||||
@ -186,11 +182,11 @@ pub const AtResult = struct {
|
||||
pub fn at(self: Self, lx: f64, ly: f64) ?AtResult {
|
||||
var sx: f64 = undefined;
|
||||
var sy: f64 = undefined;
|
||||
const node_at = self.interactive_content.node.at(lx, ly, &sx, &sy) orelse return null;
|
||||
const node = self.interactive_content.node.at(lx, ly, &sx, &sy) orelse return null;
|
||||
|
||||
const surface: ?*wlr.Surface = blk: {
|
||||
if (node_at.type == .buffer) {
|
||||
const scene_buffer = wlr.SceneBuffer.fromNode(node_at);
|
||||
if (node.type == .buffer) {
|
||||
const scene_buffer = wlr.SceneBuffer.fromNode(node);
|
||||
if (wlr.SceneSurface.fromBuffer(scene_buffer)) |scene_surface| {
|
||||
break :blk scene_surface.surface;
|
||||
}
|
||||
@ -198,19 +194,13 @@ pub fn at(self: Self, lx: f64, ly: f64) ?AtResult {
|
||||
break :blk null;
|
||||
};
|
||||
|
||||
if (SceneNodeData.fromNode(node_at)) |scene_node_data| {
|
||||
if (SceneNodeData.fromNode(node)) |scene_node_data| {
|
||||
return .{
|
||||
.node = node,
|
||||
.surface = surface,
|
||||
.sx = sx,
|
||||
.sy = sy,
|
||||
.node = switch (scene_node_data.data) {
|
||||
.view => |view| .{ .view = view },
|
||||
.layer_surface => |layer_surface| .{ .layer_surface = layer_surface },
|
||||
.lock_surface => |lock_surface| .{ .lock_surface = lock_surface },
|
||||
.xwayland_override_redirect => |xwayland_override_redirect| .{
|
||||
.xwayland_override_redirect = xwayland_override_redirect,
|
||||
},
|
||||
},
|
||||
.data = scene_node_data.data,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
|
@ -27,7 +27,7 @@ const LockSurface = @import("LockSurface.zig");
|
||||
const View = @import("View.zig");
|
||||
const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
|
||||
|
||||
const Data = union(enum) {
|
||||
pub const Data = union(enum) {
|
||||
view: *View,
|
||||
lock_surface: *LockSurface,
|
||||
layer_surface: *LayerSurface,
|
||||
|
@ -38,6 +38,7 @@ const LayerSurface = @import("LayerSurface.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const Mapping = @import("Mapping.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const PointerConstraint = @import("PointerConstraint.zig");
|
||||
const SeatStatus = @import("SeatStatus.zig");
|
||||
const Switch = @import("Switch.zig");
|
||||
const View = @import("View.zig");
|
||||
@ -245,8 +246,33 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
}
|
||||
self.focused = new_focus;
|
||||
|
||||
if (self.cursor.constraint) |constraint| {
|
||||
if (constraint.wlr_constraint.surface != target_surface) {
|
||||
if (constraint.state == .active) {
|
||||
log.info("deactivating pointer constraint for surface, keyboard focus lost", .{});
|
||||
constraint.deactivate();
|
||||
}
|
||||
self.cursor.constraint = null;
|
||||
}
|
||||
}
|
||||
|
||||
self.keyboardEnterOrLeave(target_surface);
|
||||
|
||||
if (target_surface) |surface| {
|
||||
const pointer_constraints = server.input_manager.pointer_constraints;
|
||||
if (pointer_constraints.constraintForSurface(surface, self.wlr_seat)) |wlr_constraint| {
|
||||
if (self.cursor.constraint) |constraint| {
|
||||
assert(constraint.wlr_constraint == wlr_constraint);
|
||||
} else {
|
||||
self.cursor.constraint = @intToPtr(*PointerConstraint, wlr_constraint.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Depending on configuration and cursor position, changing keyboard focus
|
||||
// may cause the cursor to be warped.
|
||||
self.cursor.may_need_warp = true;
|
||||
|
||||
// Inform any clients tracking status of the change
|
||||
var it = self.status_trackers.first;
|
||||
while (it) |node| : (it = node.next) node.data.sendFocusedView();
|
||||
@ -258,16 +284,8 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
pub fn keyboardEnterOrLeave(self: *Self, target_surface: ?*wlr.Surface) void {
|
||||
if (target_surface) |wlr_surface| {
|
||||
self.keyboardNotifyEnter(wlr_surface);
|
||||
|
||||
// Depending on configuration and cursor position, changing keyboard focus
|
||||
// may cause the cursor to be warped.
|
||||
self.cursor.may_need_warp = true;
|
||||
} else {
|
||||
self.wlr_seat.keyboardNotifyClearFocus();
|
||||
|
||||
// Depending on configuration and cursor position, changing keyboard focus
|
||||
// may cause the cursor to be warped.
|
||||
self.cursor.may_need_warp = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user