diff --git a/river/Cursor.zig b/river/Cursor.zig index c38c0dd..a6dba22 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -360,13 +360,13 @@ fn layerSurfaceAt( /// Find the topmost visible view surface (incl. popups) at ox,oy. fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface { // Focused views are rendered on top, so look for them first. - var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); + var it = ViewStack(View).iterator(output.views.first, output.current.tags); while (it.next()) |node| { if (!node.view.focused) continue; if (node.view.surfaceAt(ox, oy, sx, sy)) |found| return found; } - it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); + it = ViewStack(View).iterator(output.views.first, output.current.tags); while (it.next()) |node| { if (node.view.surfaceAt(ox, oy, sx, sy)) |found| return found; } diff --git a/river/Output.zig b/river/Output.zig index e4e688a..1b29db5 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -40,6 +40,11 @@ const OutputStatus = @import("OutputStatus.zig"); // that it should never be encountered during normal usage. const minimum_size = 50; +const State = struct { + /// A bit field of focused tags + tags: u32, +}; + root: *Root, wlr_output: *c.wlr_output, @@ -48,14 +53,15 @@ layers: [4]std.TailQueue(LayerSurface), /// The area left for views and other layer surfaces after applying the /// exclusive zones of exclusive layer surfaces. +/// TODO: this should be part of the output's State usable_box: Box, /// The top of the stack is the "most important" view. views: ViewStack(View), -/// A bit field of focused tags -current_focused_tags: u32, -pending_focused_tags: ?u32, +/// The double-buffered state of the output. +current: State, +pending: State, /// Number of views in "master" section of the screen. master_count: u32, @@ -98,8 +104,10 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void { self.views.init(); - self.current_focused_tags = 1 << 0; - self.pending_focused_tags = null; + self.current = .{ + .tags = 1 << 0, + }; + self.pending = self.current; self.master_count = 1; @@ -297,17 +305,12 @@ fn layoutExternal(self: *Self, visible_count: u32, output_tags: u32) !void { /// pending state, the changes are not appplied until a transaction is started /// and completed. pub fn arrangeViews(self: *Self) void { - const output_tags = if (self.pending_focused_tags) |tags| - tags - else - self.current_focused_tags; - const full_area = Box.fromWlrBox(c.wlr_output_layout_get_box(self.root.wlr_output_layout, self.wlr_output).*); // Make fullscreen views take the full area, count up views that will be // arranged by the layout. var layout_count: u32 = 0; - var it = ViewStack(View).pendingIterator(self.views.first, output_tags); + var it = ViewStack(View).pendingIterator(self.views.first, self.pending.tags); while (it.next()) |node| { const view = &node.view; if (view.pending.fullscreen) { @@ -321,9 +324,9 @@ pub fn arrangeViews(self: *Self) void { // would cause an underflow and is pointless anyway. if (layout_count == 0 or self.usable_box.width == 0 or self.usable_box.height == 0) return; - if (std.mem.eql(u8, self.layout, "full")) return layoutFull(self, layout_count, output_tags); + if (std.mem.eql(u8, self.layout, "full")) return layoutFull(self, layout_count, self.pending.tags); - layoutExternal(self, layout_count, output_tags) catch |err| { + layoutExternal(self, layout_count, self.pending.tags) catch |err| { switch (err) { LayoutError.BadExitCode => log.err(.layout, "layout command exited with non-zero return code", .{}), LayoutError.BadWindowConfiguration => log.err(.layout, "invalid window configuration", .{}), @@ -331,7 +334,7 @@ pub fn arrangeViews(self: *Self) void { else => log.err(.layout, "'{}' error while trying to use external layout", .{err}), } log.err(.layout, "falling back to internal layout", .{}); - layoutFull(self, layout_count, output_tags); + layoutFull(self, layout_count, self.pending.tags); }; } diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig index be0568f..eac2bbd 100644 --- a/river/OutputStatus.zig +++ b/river/OutputStatus.zig @@ -78,5 +78,5 @@ pub fn sendViewTags(self: Self) void { /// Send the currently focused tags of the output to the client. pub fn sendFocusedTags(self: Self) void { - c.zriver_output_status_v1_send_focused_tags(self.wl_resource, self.output.current_focused_tags); + c.zriver_output_status_v1_send_focused_tags(self.wl_resource, self.output.current.tags); } diff --git a/river/Root.zig b/river/Root.zig index 81478c8..381ce89 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -207,25 +207,24 @@ fn commitTransaction(self: *Self) void { while (output_it) |output_node| : (output_it = output_node.next) { const output = &output_node.data; - // If there were pending focused tags, make them the current focus - if (output.pending_focused_tags) |tags| { + // Apply pending state of the output + if (output.pending.tags != output.current.tags) { log.debug( .output, "changing current focus: {b:0>10} to {b:0>10}", - .{ output.current_focused_tags, tags }, + .{ output.current.tags, output.pending.tags }, ); - output.current_focused_tags = tags; - output.pending_focused_tags = null; var it = output.status_trackers.first; while (it) |node| : (it = node.next) node.data.sendFocusedTags(); } + output.current = output.pending; var view_tags_changed = false; var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32)); while (view_it.next()) |view_node| { const view = &view_node.view; - // Apply pending state + // Apply pending state of the view view.pending_serial = null; if (view.pending.tags != view.current.tags) view_tags_changed = true; view.current = view.pending; diff --git a/river/Seat.zig b/river/Seat.zig index c043b83..f1991f9 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -125,13 +125,13 @@ pub fn focus(self: *Self, _view: ?*View) void { // If the view is not currently visible, behave as if null was passed if (view) |v| { if (v.output != self.focused_output or - v.current.tags & self.focused_output.current_focused_tags == 0) view = null; + v.current.tags & self.focused_output.current.tags == 0) view = null; } // If the target view is not fullscreen or null, then a fullscreen view // will grab focus if visible. if (if (view) |v| !v.current.fullscreen else true) { - var it = ViewStack(*View).iterator(self.focus_stack.first, self.focused_output.current_focused_tags); + var it = ViewStack(*View).iterator(self.focus_stack.first, self.focused_output.current.tags); view = while (it.next()) |node| { if (node.view.output == self.focused_output and node.view.current.fullscreen) break node.view; } else view; @@ -139,7 +139,7 @@ pub fn focus(self: *Self, _view: ?*View) void { if (view == null) { // Set view to the first currently visible view in the focus stack if any - var it = ViewStack(*View).iterator(self.focus_stack.first, self.focused_output.current_focused_tags); + var it = ViewStack(*View).iterator(self.focus_stack.first, self.focused_output.current.tags); view = while (it.next()) |node| { if (node.view.output == self.focused_output) break node.view; } else null; diff --git a/river/Server.zig b/river/Server.zig index b31858a..90f6b19 100644 --- a/river/Server.zig +++ b/river/Server.zig @@ -181,7 +181,7 @@ fn handleNewXdgSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) v // The View will add itself to the output's view stack on map const output = self.input_manager.default_seat.focused_output; const node = util.gpa.create(ViewStack(View).Node) catch unreachable; - node.view.init(output, output.current_focused_tags, wlr_xdg_surface); + node.view.init(output, output.current.tags, wlr_xdg_surface); } /// This event is raised when the layer_shell recieves a new surface from a client. @@ -256,5 +256,5 @@ fn handleNewXwaylandSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv( // The View will add itself to the output's view stack on map const output = self.input_manager.default_seat.focused_output; const node = util.gpa.create(ViewStack(View).Node) catch unreachable; - node.view.init(output, output.current_focused_tags, wlr_xwayland_surface); + node.view.init(output, output.current.tags, wlr_xwayland_surface); } diff --git a/river/View.zig b/river/View.zig index 8b99481..cdd7d02 100644 --- a/river/View.zig +++ b/river/View.zig @@ -107,7 +107,6 @@ pub fn init(self: *Self, output: *Output, tags: u32, surface: var) void { .float = false, .fullscreen = false, }; - self.pending = self.current; self.pending_serial = null; diff --git a/river/command/focus_view.zig b/river/command/focus_view.zig index 134aa35..2c66045 100644 --- a/river/command/focus_view.zig +++ b/river/command/focus_view.zig @@ -44,8 +44,8 @@ pub fn focusView( // 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); var it = switch (direction) { - .Next => ViewStack(View).iterator(focused_node, output.current_focused_tags), - .Prev => ViewStack(View).reverseIterator(focused_node, output.current_focused_tags), + .Next => ViewStack(View).iterator(focused_node, output.current.tags), + .Prev => ViewStack(View).reverseIterator(focused_node, output.current.tags), }; // Skip past the focused node @@ -60,8 +60,8 @@ pub fn focusView( // There is either no currently focused view or the last visible view in the // stack is focused and we need to wrap. var it = switch (direction) { - .Next => ViewStack(View).iterator(output.views.first, output.current_focused_tags), - .Prev => ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags), + .Next => ViewStack(View).iterator(output.views.first, output.current.tags), + .Prev => ViewStack(View).reverseIterator(output.views.last, output.current.tags), }; seat.focus(if (it.next()) |node| &node.view else null); diff --git a/river/command/tags.zig b/river/command/tags.zig index 5c14aaa..795129c 100644 --- a/river/command/tags.zig +++ b/river/command/tags.zig @@ -28,8 +28,8 @@ pub fn setFocusedTags( out: *?[]const u8, ) Error!void { const tags = try parseTags(allocator, args, out); - if (seat.focused_output.current_focused_tags != tags) { - seat.focused_output.pending_focused_tags = tags; + if (seat.focused_output.pending.tags != tags) { + seat.focused_output.pending.tags = tags; seat.input_manager.server.root.arrange(); } } @@ -57,9 +57,9 @@ pub fn toggleFocusedTags( ) Error!void { const tags = try parseTags(allocator, args, out); const output = seat.focused_output; - const new_focused_tags = output.current_focused_tags ^ tags; + const new_focused_tags = output.pending.tags ^ tags; if (new_focused_tags != 0) { - output.pending_focused_tags = new_focused_tags; + output.pending.tags = new_focused_tags; seat.input_manager.server.root.arrange(); } } diff --git a/river/command/zoom.zig b/river/command/zoom.zig index 5c17260..69ba58a 100644 --- a/river/command/zoom.zig +++ b/river/command/zoom.zig @@ -41,7 +41,7 @@ pub fn zoom( // If the the first view that is part of the layout is focused, zoom // the next view in the layout. Otherwise zoom the focused view. - var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); + 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; diff --git a/river/render.zig b/river/render.zig index 975db52..16d9d97 100644 --- a/river/render.zig +++ b/river/render.zig @@ -57,7 +57,7 @@ pub fn renderOutput(output: *Output) void { c.wlr_renderer_begin(wlr_renderer, width, height); // Find the first visible fullscreen view in the stack if there is one - var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); + var it = ViewStack(View).iterator(output.views.first, output.current.tags); const fullscreen_view = while (it.next()) |node| { if (node.view.current.fullscreen) break &node.view; } else null; @@ -76,7 +76,7 @@ pub fn renderOutput(output: *Output) void { renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); // The first view in the list is "on top" so iterate in reverse. - it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags); + it = ViewStack(View).reverseIterator(output.views.last, output.current.tags); while (it.next()) |node| { const view = &node.view; @@ -92,7 +92,7 @@ pub fn renderOutput(output: *Output) void { } // Render focused views - it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags); + it = ViewStack(View).reverseIterator(output.views.last, output.current.tags); while (it.next()) |node| { const view = &node.view;