river: rework core data structures & transactions

This commit is contained in:
Isaac Freund
2023-02-24 19:28:37 +01:00
parent f5dc67cfc1
commit be4330288d
34 changed files with 1027 additions and 1543 deletions

View File

@ -41,7 +41,6 @@ const Output = @import("Output.zig");
const SeatStatus = @import("SeatStatus.zig");
const Switch = @import("Switch.zig");
const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack;
const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
const log = std.log.scoped(.seat);
@ -73,16 +72,11 @@ repeating_mapping: ?*const Mapping = null,
keyboard_groups: std.TailQueue(KeyboardGroup) = .{},
/// Currently focused output, may be the noop output if no real output
/// is currently available for focus.
focused_output: *Output,
/// Currently focused output. Null only when there are no outputs at all.
focused_output: ?*Output = null,
focused: FocusTarget = .none,
/// Stack of views in most recently focused order
/// If there is a currently focused view, it is on top.
focus_stack: ViewStack(*View) = .{},
/// List of status tracking objects relaying changes to this seat to clients.
status_trackers: std.SinglyLinkedList(SeatStatus) = .{},
@ -110,7 +104,6 @@ pub fn init(self: *Self, name: [*:0]const u8) !void {
self.* = .{
// This will be automatically destroyed when the display is destroyed
.wlr_seat = try wlr.Seat.create(server.wl_server, name),
.focused_output = &server.root.noop_output,
.mapping_repeat_timer = mapping_repeat_timer,
};
self.wlr_seat.data = @ptrToInt(self);
@ -136,11 +129,6 @@ pub fn deinit(self: *Self) void {
node.data.destroy();
}
while (self.focus_stack.first) |node| {
self.focus_stack.remove(node);
util.gpa.destroy(node);
}
self.request_set_selection.link.remove();
self.request_start_drag.link.remove();
self.start_drag.link.remove();
@ -149,14 +137,17 @@ pub fn deinit(self: *Self) void {
}
/// Set the current focus. If a visible view is passed it will be focused.
/// If null is passed, the first visible view in the focus stack will be focused.
/// If null is passed, the top view in the stack of the focused output will be focused.
pub fn focus(self: *Self, _target: ?*View) void {
var target = _target;
// Views may not recieve focus while locked.
// Don't change focus if there are no outputs.
if (self.focused_output == null) return;
// Views may not receive focus while locked.
if (server.lock_manager.state != .unlocked) return;
// While a layer surface is exclusively focused, views may not recieve focus
// While a layer surface is exclusively focused, views may not receive focus
if (self.focused == .layer) {
const wlr_layer_surface = self.focused.layer.scene_layer_surface.layer_surface;
if (wlr_layer_surface.current.keyboard_interactive == .exclusive and
@ -167,58 +158,47 @@ pub fn focus(self: *Self, _target: ?*View) void {
}
if (target) |view| {
// If the view is not currently visible, behave as if null was passed
if (view.pending.tags & view.output.pending.tags == 0) {
if (view.pending.tags & view.pending.output.?.pending.tags == 0) {
// If the view is not currently visible, behave as if null was passed
target = null;
} else {
} else if (view.pending.output.? != self.focused_output.?) {
// If the view is not on the currently focused output, focus it
if (view.output != self.focused_output) self.focusOutput(view.output);
self.focusOutput(view.pending.output.?);
}
}
// If the target view is not fullscreen or null, then a fullscreen view
// will grab focus if visible.
if (if (target) |v| !v.pending.fullscreen else true) {
const tags = self.focused_output.pending.tags;
var it = ViewStack(*View).iter(self.focus_stack.first, .forward, tags, pendingFilter);
target = while (it.next()) |view| {
if (view.output == self.focused_output and view.pending.fullscreen) break view;
} else target;
{
var it = self.focused_output.?.pending.focus_stack.iterator(.forward);
while (it.next()) |view| {
if (view.pending.fullscreen and
view.pending.tags & self.focused_output.?.pending.tags != 0)
{
target = view;
break;
}
}
}
// If null, set the target to the first currently visible view in the focus stack if any
if (target == null) {
// Set view to the first currently visible view in the focus stack if any
const tags = self.focused_output.pending.tags;
var it = ViewStack(*View).iter(self.focus_stack.first, .forward, tags, pendingFilter);
var it = self.focused_output.?.pending.focus_stack.iterator(.forward);
target = while (it.next()) |view| {
if (view.output == self.focused_output) break view;
if (view.pending.tags & self.focused_output.?.pending.tags != 0) {
break view;
}
} else null;
}
// Focus the target view or clear the focus if target is null
if (target) |view| {
// Find the node for this view in the focus stack and move it to the top.
var it = self.focus_stack.first;
while (it) |node| : (it = node.next) {
if (node.view == view) {
self.focus_stack.remove(node);
self.focus_stack.push(node);
break;
}
} else {
// A node is added when new Views are mapped in Seat.handleViewMap()
unreachable;
}
view.pending_focus_stack_link.remove();
self.focused_output.?.pending.focus_stack.prepend(view);
self.setFocusRaw(.{ .view = view });
} else {
self.setFocusRaw(.{ .none = {} });
}
}
fn pendingFilter(view: *View, filter_tags: u32) bool {
return view.tree.node.enabled and view.pending.tags & filter_tags != 0;
}
/// Switch focus to the target, handling unfocus and input inhibition
/// properly. This should only be called directly if dealing with layers or
/// override redirect xwayland views.
@ -252,7 +232,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
switch (new_focus) {
.view => |target_view| {
assert(server.lock_manager.state != .locked);
assert(self.focused_output == target_view.output);
assert(self.focused_output == target_view.pending.output);
if (target_view.pending.focus == 0) target_view.setActivated(true);
target_view.pending.focus += 1;
target_view.pending.urgent = false;
@ -314,48 +294,26 @@ fn keyboardNotifyEnter(self: *Self, wlr_surface: *wlr.Surface) void {
}
/// Focus the given output, notifying any listening clients of the change.
pub fn focusOutput(self: *Self, output: *Output) void {
pub fn focusOutput(self: *Self, output: ?*Output) void {
if (self.focused_output == output) return;
var it = self.status_trackers.first;
while (it) |node| : (it = node.next) node.data.sendOutput(.unfocused);
if (self.focused_output) |old| {
var it = self.status_trackers.first;
while (it) |node| : (it = node.next) node.data.sendOutput(old, .unfocused);
}
self.focused_output = output;
it = self.status_trackers.first;
while (it) |node| : (it = node.next) node.data.sendOutput(.focused);
if (self.focused_output) |new| {
var it = self.status_trackers.first;
while (it) |node| : (it = node.next) node.data.sendOutput(new, .focused);
}
}
pub fn handleActivity(self: Self) void {
server.input_manager.idle_notifier.notifyActivity(self.wlr_seat);
}
pub fn handleViewMap(self: *Self, view: *View) !void {
const new_focus_node = try util.gpa.create(ViewStack(*View).Node);
new_focus_node.view = view;
self.focus_stack.append(new_focus_node);
self.focus(view);
}
/// Handle the unmapping of a view, removing it from the focus stack and
/// setting the focus if needed.
pub fn handleViewUnmap(self: *Self, view: *View) void {
// Remove the node from the focus stack and destroy it.
var it = self.focus_stack.first;
while (it) |node| : (it = node.next) {
if (node.view == view) {
self.focus_stack.remove(node);
util.gpa.destroy(node);
break;
}
}
self.cursor.handleViewUnmap(view);
// If the unmapped view is focused, choose a new focus
if (self.focused == .view and self.focused.view == view) self.focus(null);
}
pub fn enterMode(self: *Self, mode_id: u32) void {
self.mode_id = mode_id;