diff --git a/src/command.zig b/src/command.zig index 1f2b483..b5c237a 100644 --- a/src/command.zig +++ b/src/command.zig @@ -3,6 +3,7 @@ const c = @import("c.zig"); const Log = @import("log.zig").Log; const Server = @import("server.zig").Server; +const View = @import("view.zig").View; const ViewStack = @import("view_stack.zig").ViewStack; pub const Arg = union { @@ -60,7 +61,7 @@ pub fn modifyMasterFactor(server: *Server, arg: Arg) void { pub fn zoom(server: *Server, arg: Arg) void { if (server.root.focused_view) |current_focus| { const output = server.root.focusedOutput(); - const node = @fieldParentPtr(ViewStack.Node, "view", current_focus); + const node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); if (node != output.views.first) { output.views.remove(node); output.views.push(node); diff --git a/src/output.zig b/src/output.zig index 3aa1ae8..23e9e88 100644 --- a/src/output.zig +++ b/src/output.zig @@ -6,6 +6,7 @@ const Box = @import("box.zig").Box; const LayerSurface = @import("layer_surface.zig").LayerSurface; const Log = @import("log.zig").Log; const Root = @import("root.zig").Root; +const View = @import("view.zig").View; const ViewStack = @import("view_stack.zig").ViewStack; pub const Output = struct { @@ -22,7 +23,7 @@ pub const Output = struct { usable_box: Box, /// The top of the stack is the "most important" view. - views: ViewStack, + views: ViewStack(View), /// A bit field of focused tags current_focused_tags: u32, @@ -100,7 +101,7 @@ pub const Output = struct { /// Add a new view to the output. arrangeViews() will be called by the view /// when it is mapped. pub fn addView(self: *Self, wlr_xdg_surface: *c.wlr_xdg_surface) void { - const node = self.root.server.allocator.create(ViewStack.Node) catch unreachable; + const node = self.root.server.allocator.create(ViewStack(View).Node) catch unreachable; node.view.init(self, wlr_xdg_surface, self.current_focused_tags); self.views.push(node); } @@ -129,7 +130,7 @@ pub const Output = struct { const visible_count = blk: { var count: u32 = 0; - var it = ViewStack.pendingIterator(self.views.first, output_tags); + var it = ViewStack(View).pendingIterator(self.views.first, output_tags); while (it.next() != null) count += 1; break :blk count; }; @@ -157,8 +158,9 @@ pub const Output = struct { } var i: u32 = 0; - var it = ViewStack.pendingIterator(self.views.first, output_tags); - while (it.next()) |view| { + var it = ViewStack(View).pendingIterator(self.views.first, output_tags); + while (it.next()) |node| { + const view = &node.view; if (i < master_count) { // Add the remainder to the first master to ensure every pixel of height is used const master_height = @divTrunc(layout_height, master_count); diff --git a/src/render.zig b/src/render.zig index a330a5b..074228c 100644 --- a/src/render.zig +++ b/src/render.zig @@ -44,8 +44,9 @@ pub fn renderOutput(output: *Output) void { renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now, ox, oy); // The first view in the list is "on top" so iterate in reverse. - var it = ViewStack.reverseIterator(output.views.last, output.current_focused_tags); - while (it.next()) |view| { + var it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags); + while (it.next()) |node| { + const view = &node.view; // This check prevents a race condition when a frame is requested // between mapping of a view and the first configure being handled. if (view.current_box.width == 0 or view.current_box.height == 0) { diff --git a/src/root.zig b/src/root.zig index 2ac8074..a5f2bf0 100644 --- a/src/root.zig +++ b/src/root.zig @@ -71,10 +71,10 @@ pub const Root = struct { var output_it = self.outputs.first; while (output_it) |node| : (output_it = node.next) { const output = &node.data; - var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); - while (view_it.next()) |view| { - if (view.isAt(lx, ly, surface, sx, sy)) { - return view; + var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF); + while (view_it.next()) |view_node| { + if (view_node.view.isAt(lx, ly, surface, sx, sy)) { + return &view_node.view; } } } @@ -95,22 +95,22 @@ pub const Root = struct { const output = self.focusedOutput(); if (self.focused_view) |current_focus| { // If there is a currently focused view, focus the next visible view in the stack. - const current_node = @fieldParentPtr(ViewStack.Node, "view", current_focus); - var it = ViewStack.iterator(current_node, output.current_focused_tags); + const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); + var it = ViewStack(View).iterator(current_node, output.current_focused_tags); // Skip past the current node _ = it.next(); // Focus the next visible node if there is one - if (it.next()) |view| { - view.focus(view.wlr_xdg_surface.surface); + if (it.next()) |node| { + node.view.focus(node.view.wlr_xdg_surface.surface); return; } } // There is either no currently focused view or the last visible view in the // stack is focused and we need to wrap. - var it = ViewStack.iterator(output.views.first, output.current_focused_tags); - if (it.next()) |view| { - view.focus(view.wlr_xdg_surface.surface); + var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); + if (it.next()) |node| { + node.view.focus(node.view.wlr_xdg_surface.surface); } else { // Otherwise clear the focus since there are no visible views self.clearFocus(); @@ -123,22 +123,22 @@ pub const Root = struct { const output = self.focusedOutput(); if (self.focused_view) |current_focus| { // If there is a currently focused view, focus the previous visible view in the stack. - const current_node = @fieldParentPtr(ViewStack.Node, "view", current_focus); - var it = ViewStack.reverseIterator(current_node, output.current_focused_tags); + const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); + var it = ViewStack(View).reverseIterator(current_node, output.current_focused_tags); // Skip past the current node _ = it.next(); // Focus the previous visible node if there is one - if (it.next()) |view| { - view.focus(view.wlr_xdg_surface.surface); + if (it.next()) |node| { + node.view.focus(node.view.wlr_xdg_surface.surface); return; } } // There is either no currently focused view or the first visible view in the // stack is focused and we need to wrap. - var it = ViewStack.reverseIterator(output.views.last, output.current_focused_tags); - if (it.next()) |view| { - view.focus(view.wlr_xdg_surface.surface); + var it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags); + if (it.next()) |node| { + node.view.focus(node.view.wlr_xdg_surface.surface); } else { // Otherwise clear the focus since there are no visible views self.clearFocus(); @@ -166,8 +166,9 @@ pub const Root = struct { var output_it = self.outputs.first; while (output_it) |node| : (output_it = node.next) { const output = &node.data; - var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); - while (view_it.next()) |view| { + var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF); + while (view_it.next()) |view_node| { + const view = &view_node.view; // Clear the serial in case this transaction is interrupting a prior one. view.pending_serial = null; @@ -259,8 +260,9 @@ pub const Root = struct { self.focusNextView(); } - var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); - while (view_it.next()) |view| { + var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF); + while (view_it.next()) |view_node| { + const view = &view_node.view; // Ensure that all pending state is cleared view.pending_serial = null; if (view.pending_box) |state| { diff --git a/src/view.zig b/src/view.zig index 824f98a..191bcf8 100644 --- a/src/view.zig +++ b/src/view.zig @@ -156,7 +156,7 @@ pub const View = struct { const view = @fieldParentPtr(View, "listen_destroy", listener.?); const output = view.output; - const node = @fieldParentPtr(ViewStack.Node, "view", view); + const node = @fieldParentPtr(ViewStack(View).Node, "view", view); output.views.remove(node); output.root.server.allocator.destroy(node); } diff --git a/src/view_stack.zig b/src/view_stack.zig index a729837..c1fe4e1 100644 --- a/src/view_stack.zig +++ b/src/view_stack.zig @@ -1,147 +1,151 @@ const View = @import("view.zig").View; /// A specialized doubly-linked stack that allows for filtered iteration -/// over the nodes -pub const ViewStack = struct { - const Self = @This(); - - pub const Node = struct { - /// Previous/next nodes in the stack - prev: ?*Node, - next: ?*Node, - - /// The view stored in this node - view: View, - }; - - /// Top/bottom nodes in the stack - first: ?*Node, - last: ?*Node, - - /// Initialize an undefined stack - pub fn init(self: *Self) void { - self.first = null; - self.last = null; +/// over the nodes. T must be View or *View. +pub fn ViewStack(comptime T: type) type { + if (!(T == View or T == *View)) { + @compileError("ViewStack: T must be View or *View"); } + return struct { + const Self = @This(); - /// Add a node to the top of the stack. - pub fn push(self: *Self, new_node: *Node) void { - // Set the prev/next pointers of the new node - new_node.prev = null; - new_node.next = self.first; + pub const Node = struct { + /// Previous/next nodes in the stack + prev: ?*Node, + next: ?*Node, - if (self.first) |first| { - // If the list is not empty, set the prev pointer of the current - // first node to the new node. - first.prev = new_node; - } else { - // If the list is empty set the last pointer to the new node. - self.last = new_node; + /// The view stored in this node + view: T, + }; + + /// Top/bottom nodes in the stack + first: ?*Node, + last: ?*Node, + + /// Initialize an undefined stack + pub fn init(self: *Self) void { + self.first = null; + self.last = null; } - // Set the first pointer to the new node - self.first = new_node; - } + /// Add a node to the top of the stack. + pub fn push(self: *Self, new_node: *Node) void { + // Set the prev/next pointers of the new node + new_node.prev = null; + new_node.next = self.first; - /// Remove a node from the view stack. This removes it from the stack of - /// all views as well as the stack of visible ones. - pub fn remove(self: *Self, target_node: *Node) void { - // Set the previous node/list head to the next pointer - if (target_node.prev) |prev_node| { - prev_node.next = target_node.next; - } else { - self.first = target_node.next; - } - - // Set the next node/list tail to the previous pointer - if (target_node.next) |next_node| { - next_node.prev = target_node.prev; - } else { - self.last = target_node.prev; - } - } - - const Iterator = struct { - it: ?*Node, - tags: u32, - reverse: bool, - pending: bool, - - /// Returns the next node in iteration order, or null if done. - /// This function is horribly ugly, but it's well tested below. - pub fn next(self: *Iterator) ?*View { - while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { - if (node.view.mapped and if (self.pending) - if (node.view.pending_tags) |pending_tags| - self.tags & pending_tags != 0 - else - self.tags & node.view.current_tags != 0 - else - self.tags & node.view.current_tags != 0) { - const ret = &node.view; - self.it = if (self.reverse) node.prev else node.next; - return ret; - } + if (self.first) |first| { + // If the list is not empty, set the prev pointer of the current + // first node to the new node. + first.prev = new_node; + } else { + // If the list is empty set the last pointer to the new node. + self.last = new_node; } - return null; + + // Set the first pointer to the new node + self.first = new_node; + } + + /// Remove a node from the view stack. This removes it from the stack of + /// all views as well as the stack of visible ones. + pub fn remove(self: *Self, target_node: *Node) void { + // Set the previous node/list head to the next pointer + if (target_node.prev) |prev_node| { + prev_node.next = target_node.next; + } else { + self.first = target_node.next; + } + + // Set the next node/list tail to the previous pointer + if (target_node.next) |next_node| { + next_node.prev = target_node.prev; + } else { + self.last = target_node.prev; + } + } + + const Iterator = struct { + it: ?*Node, + tags: u32, + reverse: bool, + pending: bool, + + /// Returns the next node in iteration order, or null if done. + /// This function is horribly ugly, but it's well tested below. + pub fn next(self: *Iterator) ?*Node { + while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { + if (node.view.mapped and if (self.pending) + if (node.view.pending_tags) |pending_tags| + self.tags & pending_tags != 0 + else + self.tags & node.view.current_tags != 0 + else + self.tags & node.view.current_tags != 0) { + self.it = if (self.reverse) node.prev else node.next; + return node; + } + } + return null; + } + }; + + /// Returns an iterator starting at the passed node and filtered by + /// checking the passed tags against the current tags of each view. + /// Unmapped views are skipped. + pub fn iterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = false, + .pending = false, + }; + } + + /// Returns a reverse iterator starting at the passed node and filtered by + /// checking the passed tags against the current tags of each view. + /// Unmapped views are skipped. + pub fn reverseIterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = true, + .pending = false, + }; + } + + /// Returns an iterator starting at the passed node and filtered by + /// checking the passed tags against the pending tags of each view. + /// If a view has no pending tags, the current tags are used. Unmapped + /// views are skipped. + pub fn pendingIterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = false, + .pending = true, + }; } }; - - /// Returns an iterator starting at the passed node and filtered by - /// checking the passed tags against the current tags of each view. - /// Unmapped views are skipped. - pub fn iterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = false, - .pending = false, - }; - } - - /// Returns a reverse iterator starting at the passed node and filtered by - /// checking the passed tags against the current tags of each view. - /// Unmapped views are skipped. - pub fn reverseIterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = true, - .pending = false, - }; - } - - /// Returns an iterator starting at the passed node and filtered by - /// checking the passed tags against the pending tags of each view. - /// If a view has no pending tags, the current tags are used. Unmapped - /// views are skipped. - pub fn pendingIterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = false, - .pending = true, - }; - } -}; +} const testing = @import("std").testing; -test "push/remove" { +test "push/remove (*View)" { const allocator = testing.allocator; - var views: ViewStack = undefined; + var views: ViewStack(*View) = undefined; views.init(); - const one = try allocator.create(ViewStack.Node); + const one = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(one); - const two = try allocator.create(ViewStack.Node); + const two = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(two); - const three = try allocator.create(ViewStack.Node); + const three = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(three); - const four = try allocator.create(ViewStack.Node); + const four = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(four); - const five = try allocator.create(ViewStack.Node); + const five = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(five); views.push(three); // {3} @@ -255,40 +259,40 @@ test "push/remove" { testing.expect(views.last == null); } -test "iteration" { +test "iteration (View)" { const allocator = testing.allocator; - var views: ViewStack = undefined; + var views: ViewStack(View) = undefined; views.init(); - const one_a_pb = try allocator.create(ViewStack.Node); + const one_a_pb = try allocator.create(ViewStack(View).Node); defer allocator.destroy(one_a_pb); one_a_pb.view.mapped = true; one_a_pb.view.current_tags = 1 << 0; one_a_pb.view.pending_tags = 1 << 1; - const two_a = try allocator.create(ViewStack.Node); + const two_a = try allocator.create(ViewStack(View).Node); defer allocator.destroy(two_a); two_a.view.mapped = true; two_a.view.current_tags = 1 << 0; - const three_b_pa = try allocator.create(ViewStack.Node); + const three_b_pa = try allocator.create(ViewStack(View).Node); defer allocator.destroy(three_b_pa); three_b_pa.view.mapped = true; three_b_pa.view.current_tags = 1 << 1; three_b_pa.view.pending_tags = 1 << 0; - const four_b = try allocator.create(ViewStack.Node); + const four_b = try allocator.create(ViewStack(View).Node); defer allocator.destroy(four_b); four_b.view.mapped = true; four_b.view.current_tags = 1 << 1; - const five_b = try allocator.create(ViewStack.Node); + const five_b = try allocator.create(ViewStack(View).Node); defer allocator.destroy(five_b); five_b.view.mapped = true; five_b.view.current_tags = 1 << 1; - const unmapped_1 = try allocator.create(ViewStack.Node); + const unmapped_1 = try allocator.create(ViewStack(View).Node); defer allocator.destroy(unmapped_1); unmapped_1.view.mapped = false; @@ -301,92 +305,92 @@ test "iteration" { // Iteration over all tags { - var it = ViewStack.iterator(views.first, 0xFFFFFFFF); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).iterator(views.first, 0xFFFFFFFF); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over 'a' tags { - var it = ViewStack.iterator(views.first, 1 << 0); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &one_a_pb.view); + var it = ViewStack(View).iterator(views.first, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); testing.expect(it.next() == null); } // Iteration over 'b' tags { - var it = ViewStack.iterator(views.first, 1 << 1); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).iterator(views.first, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over tags that aren't present { - var it = ViewStack.iterator(views.first, 1 << 2); + var it = ViewStack(View).iterator(views.first, 1 << 2); testing.expect(it.next() == null); } // Reverse iteration over all tags { - var it = ViewStack.reverseIterator(views.last, 0xFFFFFFFF); - testing.expect(it.next() == &three_b_pa.view); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &two_a.view); + var it = ViewStack(View).reverseIterator(views.last, 0xFFFFFFFF); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); testing.expect(it.next() == null); } // Reverse iteration over 'a' tags { - var it = ViewStack.reverseIterator(views.last, 1 << 0); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &two_a.view); + var it = ViewStack(View).reverseIterator(views.last, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); testing.expect(it.next() == null); } // Reverse iteration over 'b' tags { - var it = ViewStack.reverseIterator(views.last, 1 << 1); - testing.expect(it.next() == &three_b_pa.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &five_b.view); + var it = ViewStack(View).reverseIterator(views.last, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); testing.expect(it.next() == null); } // Reverse iteration over tags that aren't present { - var it = ViewStack.reverseIterator(views.first, 1 << 2); + var it = ViewStack(View).reverseIterator(views.first, 1 << 2); testing.expect(it.next() == null); } // Iteration over (pending) 'a' tags { - var it = ViewStack.pendingIterator(views.first, 1 << 0); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).pendingIterator(views.first, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over (pending) 'b' tags { - var it = ViewStack.pendingIterator(views.first, 1 << 1); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &one_a_pb.view); + var it = ViewStack(View).pendingIterator(views.first, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); testing.expect(it.next() == null); } // Iteration over (pending) tags that aren't present { - var it = ViewStack.pendingIterator(views.first, 1 << 2); + var it = ViewStack(View).pendingIterator(views.first, 1 << 2); testing.expect(it.next() == null); } }