From 101f47d78fa79d0d709c754a2323d72a2ef29752 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 15 Jul 2020 14:15:17 +0200 Subject: [PATCH] code: use a tagged union to store focus This simplifies the code and is more robust than two separate pointers. --- river/LayerSurface.zig | 7 +--- river/Output.zig | 10 ++--- river/Seat.zig | 59 +++++++---------------------- river/SeatStatus.zig | 6 +-- river/command/close.zig | 2 +- river/command/focus_view.zig | 6 +-- river/command/send_to_output.zig | 8 ++-- river/command/tags.zig | 14 +++---- river/command/toggle_float.zig | 4 +- river/command/toggle_fullscreen.zig | 6 +-- river/command/zoom.zig | 12 +++--- 11 files changed, 48 insertions(+), 86 deletions(-) diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig index 354173f..1f5971f 100644 --- a/river/LayerSurface.zig +++ b/river/LayerSurface.zig @@ -139,11 +139,8 @@ fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void { var it = self.output.root.server.input_manager.seats.first; while (it) |node| : (it = node.next) { const seat = &node.data; - if (seat.focused_layer) |current_focus| { - if (current_focus == self) { - seat.setFocusRaw(.{ .none = {} }); - } - } + if (seat.focused == .layer and seat.focused.layer == self) + seat.setFocusRaw(.{ .none = {} }); } // This gives exclusive focus to a keyboard interactive top or overlay layer diff --git a/river/Output.zig b/river/Output.zig index 01d1d24..c7b576e 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -393,18 +393,16 @@ pub fn arrangeLayers(self: *Self) void { const seat = &node.data; // Only grab focus of seats which have the output focused - if (seat.focused_output != self) { - continue; - } + if (seat.focused_output != self) continue; if (topmost_surface) |to_focus| { // If we found a surface that requires focus, grab the focus of all // seats. seat.setFocusRaw(.{ .layer = to_focus }); - } else if (seat.focused_layer) |current_focus| { + } else if (seat.focused == .layer) { // If the seat is currently focusing a layer without keyboard - // interactivity, clear the focused layer. - if (!current_focus.wlr_layer_surface.current.keyboard_interactive) { + // interactivity, stop focusing that layer. + if (!seat.focused.layer.wlr_layer_surface.current.keyboard_interactive) { seat.setFocusRaw(.{ .none = {} }); seat.focus(null); } diff --git a/river/Seat.zig b/river/Seat.zig index 3493cbd..c3d75db 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -33,8 +33,6 @@ const SeatStatus = @import("SeatStatus.zig"); const View = @import("View.zig"); const ViewStack = @import("view_stack.zig").ViewStack; -// TODO: remove none variant, unify focused_view and focused_layer fields -// with type ?FocusTarget const FocusTarget = union(enum) { view: *View, layer: *LayerSurface, @@ -56,17 +54,13 @@ mode_id: usize, /// Currently focused output, may be the noop output if no focused_output: *Output, -/// Currently focused view if any -focused_view: ?*View, +/// Currently focused view/layer surface if any +focused: FocusTarget, /// Stack of views in most recently focused order /// If there is a currently focused view, it is on top. focus_stack: ViewStack(*View), -/// Currently focused layer, if any. While this is non-null, no views may -/// recieve focus. -focused_layer: ?*LayerSurface, - /// List of status tracking objects relaying changes to this seat to clients. status_trackers: std.SinglyLinkedList(SeatStatus), @@ -91,12 +85,10 @@ pub fn init(self: *Self, input_manager: *InputManager, name: [*:0]const u8) !voi self.focused_output = &self.input_manager.server.root.noop_output; - self.focused_view = null; + self.focused = .none; self.focus_stack.init(); - self.focused_layer = null; - self.status_trackers = std.SinglyLinkedList(SeatStatus).init(); self.pointer_modifier = false; @@ -122,10 +114,7 @@ pub fn focus(self: *Self, _view: ?*View) void { var view = _view; // While a layer surface is focused, views may not recieve focus - if (self.focused_layer != null) { - std.debug.assert(self.focused_view == null); - return; - } + if (self.focused == .layer) return; // If the view is not currently visible, behave as if null was passed if (view) |v| { @@ -177,16 +166,12 @@ pub fn focus(self: *Self, _view: ?*View) void { /// Switch focus to the target, handling unfocus and input inhibition /// properly. This should only be called directly if dealing with layers. -pub fn setFocusRaw(self: *Self, focus_target: FocusTarget) void { +pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { // If the target is already focused, do nothing - if (switch (focus_target) { - .view => |target_view| target_view == self.focused_view, - .layer => |target_layer| target_layer == self.focused_layer, - .none => false, - }) return; + if (std.meta.eql(new_focus, self.focused)) return; // Obtain the target wlr_surface - const target_wlr_surface = switch (focus_target) { + const target_wlr_surface = switch (new_focus) { .view => |target_view| target_view.wlr_surface.?, .layer => |target_layer| target_layer.wlr_layer_surface.surface.?, .none => null, @@ -195,35 +180,21 @@ pub fn setFocusRaw(self: *Self, focus_target: FocusTarget) void { // If input is not allowed on the target surface (e.g. due to an active // input inhibitor) do not set focus. If there is no target surface we // still clear the focus. - if (if (target_wlr_surface) |wlr_surface| - self.input_manager.inputAllowed(wlr_surface) - else - true) { + if (if (target_wlr_surface) |wlr_surface| self.input_manager.inputAllowed(wlr_surface) else true) { // First clear the current focus - if (self.focused_view) |current_focus| { - std.debug.assert(self.focused_layer == null); - current_focus.setFocused(false); - self.focused_view = null; - } - if (self.focused_layer) |current_focus| { - std.debug.assert(self.focused_view == null); - self.focused_layer = null; - } + if (self.focused == .view) self.focused.view.setFocused(false); c.wlr_seat_keyboard_clear_focus(self.wlr_seat); // Set the new focus - switch (focus_target) { + switch (new_focus) { .view => |target_view| { std.debug.assert(self.focused_output == target_view.output); target_view.setFocused(true); - self.focused_view = target_view; - }, - .layer => |target_layer| blk: { - std.debug.assert(self.focused_output == target_layer.output); - self.focused_layer = target_layer; }, + .layer => |target_layer| std.debug.assert(self.focused_output == target_layer.output), .none => {}, } + self.focused = new_focus; // Tell wlroots to send the new keyboard focus if we have a target if (target_wlr_surface) |wlr_surface| { @@ -270,11 +241,7 @@ pub fn handleViewUnmap(self: *Self, view: *View) void { } // If the unmapped view is focused, choose a new focus - if (self.focused_view) |current_focus| { - if (current_focus == view) { - self.focus(null); - } - } + if (self.focused == .view and self.focused.view == view) self.focus(null); } /// Handle any user-defined mapping for the passed keysym and modifiers diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig index 561adef..01d4f78 100644 --- a/river/SeatStatus.zig +++ b/river/SeatStatus.zig @@ -75,8 +75,6 @@ pub fn sendOutput(self: Self, state: FocusState) void { } pub fn sendFocusedView(self: Self) void { - c.zriver_seat_status_v1_send_focused_view(self.wl_resource, if (self.seat.focused_view) |v| - v.getTitle() - else - ""); + const title: [*:0]const u8 = if (self.seat.focused == .view) self.seat.focused.view.getTitle() else ""; + c.zriver_seat_status_v1_send_focused_view(self.wl_resource, title); } diff --git a/river/command/close.zig b/river/command/close.zig index be436d4..b36e7b1 100644 --- a/river/command/close.zig +++ b/river/command/close.zig @@ -29,5 +29,5 @@ pub fn close( ) Error!void { // Note: we don't call arrange() here as it will be called // automatically when the view is unmapped. - if (seat.focused_view) |view| view.close(); + if (seat.focused == .view) seat.focused.view.close(); } diff --git a/river/command/focus_view.zig b/river/command/focus_view.zig index 44fd214..cc1b26e 100644 --- a/river/command/focus_view.zig +++ b/river/command/focus_view.zig @@ -37,12 +37,12 @@ pub fn focusView( const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection; const output = seat.focused_output; - if (seat.focused_view) |current_focus| { + if (seat.focused == .view) { // If the focused view is fullscreen, do nothing - if (current_focus.current.fullscreen) return; + if (seat.focused.view.current.fullscreen) return; // If there is a currently focused view, focus the next visible view in the stack. - const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); + const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view); var it = switch (direction) { .next => ViewStack(View).iterator(focused_node, output.current.tags), .previous => ViewStack(View).reverseIterator(focused_node, output.current.tags), diff --git a/river/command/send_to_output.zig b/river/command/send_to_output.zig index c0e3ad1..86ca92d 100644 --- a/river/command/send_to_output.zig +++ b/river/command/send_to_output.zig @@ -36,22 +36,22 @@ pub fn sendToOutput( const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection; const root = &seat.input_manager.server.root; - if (seat.focused_view) |view| { + if (seat.focused == .view) { // If the noop output is focused, there is nowhere to send the view - if (view.output == &root.noop_output) { + if (seat.focused_output == &root.noop_output) { std.debug.assert(root.outputs.len == 0); return; } // Send to the next/prev output in the list if there is one, else wrap - const current_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", view.output); + const current_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output); const destination_output = switch (direction) { .next => if (current_node.next) |node| &node.data else &root.outputs.first.?.data, .previous => if (current_node.prev) |node| &node.data else &root.outputs.last.?.data, }; // Move the view to the target output - view.sendToOutput(destination_output); + seat.focused.view.sendToOutput(destination_output); // Handle the change and focus whatever's next in the focus stack root.arrange(); diff --git a/river/command/tags.zig b/river/command/tags.zig index 795129c..4063625 100644 --- a/river/command/tags.zig +++ b/river/command/tags.zig @@ -42,9 +42,9 @@ pub fn setViewTags( out: *?[]const u8, ) Error!void { const tags = try parseTags(allocator, args, out); - if (seat.focused_view) |view| { - view.pending.tags = tags; - view.output.root.arrange(); + if (seat.focused == .view) { + seat.focused.view.pending.tags = tags; + seat.focused.view.output.root.arrange(); } } @@ -72,11 +72,11 @@ pub fn toggleViewTags( out: *?[]const u8, ) Error!void { const tags = try parseTags(allocator, args, out); - if (seat.focused_view) |view| { - const new_tags = view.current.tags ^ tags; + if (seat.focused == .view) { + const new_tags = seat.focused.view.current.tags ^ tags; if (new_tags != 0) { - view.pending.tags = new_tags; - view.output.root.arrange(); + seat.focused.view.pending.tags = new_tags; + seat.focused.view.output.root.arrange(); } } } diff --git a/river/command/toggle_float.zig b/river/command/toggle_float.zig index 38ff74f..4238469 100644 --- a/river/command/toggle_float.zig +++ b/river/command/toggle_float.zig @@ -30,7 +30,9 @@ pub fn toggleFloat( ) Error!void { if (args.len > 1) return Error.TooManyArguments; - if (seat.focused_view) |view| { + if (seat.focused == .view) { + const view = seat.focused.view; + // Don't float fullscreen views if (view.pending.fullscreen) return; diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig index 0b3acc7..5699e8a 100644 --- a/river/command/toggle_fullscreen.zig +++ b/river/command/toggle_fullscreen.zig @@ -29,8 +29,8 @@ pub fn toggleFullscreen( ) Error!void { if (args.len > 1) return Error.TooManyArguments; - if (seat.focused_view) |view| { - view.setFullscreen(!view.pending.fullscreen); - view.output.root.arrange(); + if (seat.focused == .view) { + seat.focused.view.setFullscreen(!seat.focused.view.pending.fullscreen); + seat.focused.view.output.root.arrange(); } } diff --git a/river/command/zoom.zig b/river/command/zoom.zig index 69ba58a..0b4c1c0 100644 --- a/river/command/zoom.zig +++ b/river/command/zoom.zig @@ -32,19 +32,19 @@ pub fn zoom( ) Error!void { if (args.len > 1) return Error.TooManyArguments; - if (seat.focused_view) |current_focus| { - const output = seat.focused_output; - const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); - + if (seat.focused == .view) { // Only zoom views that are part of the layout - if (current_focus.pending.float or current_focus.pending.fullscreen) return; + if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return; - // If the the first view that is part of the layout is focused, zoom + // If the first view that is part of the layout is focused, zoom // the next view in the layout. Otherwise zoom the focused view. + const output = seat.focused_output; var it = ViewStack(View).iterator(output.views.first, output.current.tags); const layout_first = while (it.next()) |node| { if (!node.view.pending.float and !node.view.pending.fullscreen) break node; } else unreachable; + + const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view); const zoom_node = if (focused_node == layout_first) blk: { while (it.next()) |node| { if (!node.view.pending.float and !node.view.pending.fullscreen) break :blk node;