Allow storing *View in ViewStack

This is done in preparation for implementing focus stacks.
This commit is contained in:
Isaac Freund 2020-04-13 17:25:39 +02:00
parent 0f52f664f2
commit b822084f39
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
6 changed files with 206 additions and 196 deletions

View File

@ -3,6 +3,7 @@ const c = @import("c.zig");
const Log = @import("log.zig").Log; const Log = @import("log.zig").Log;
const Server = @import("server.zig").Server; const Server = @import("server.zig").Server;
const View = @import("view.zig").View;
const ViewStack = @import("view_stack.zig").ViewStack; const ViewStack = @import("view_stack.zig").ViewStack;
pub const Arg = union { pub const Arg = union {
@ -60,7 +61,7 @@ pub fn modifyMasterFactor(server: *Server, arg: Arg) void {
pub fn zoom(server: *Server, arg: Arg) void { pub fn zoom(server: *Server, arg: Arg) void {
if (server.root.focused_view) |current_focus| { if (server.root.focused_view) |current_focus| {
const output = server.root.focusedOutput(); 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) { if (node != output.views.first) {
output.views.remove(node); output.views.remove(node);
output.views.push(node); output.views.push(node);

View File

@ -6,6 +6,7 @@ const Box = @import("box.zig").Box;
const LayerSurface = @import("layer_surface.zig").LayerSurface; const LayerSurface = @import("layer_surface.zig").LayerSurface;
const Log = @import("log.zig").Log; const Log = @import("log.zig").Log;
const Root = @import("root.zig").Root; const Root = @import("root.zig").Root;
const View = @import("view.zig").View;
const ViewStack = @import("view_stack.zig").ViewStack; const ViewStack = @import("view_stack.zig").ViewStack;
pub const Output = struct { pub const Output = struct {
@ -22,7 +23,7 @@ pub const Output = struct {
usable_box: Box, usable_box: Box,
/// The top of the stack is the "most important" view. /// The top of the stack is the "most important" view.
views: ViewStack, views: ViewStack(View),
/// A bit field of focused tags /// A bit field of focused tags
current_focused_tags: u32, 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 /// Add a new view to the output. arrangeViews() will be called by the view
/// when it is mapped. /// when it is mapped.
pub fn addView(self: *Self, wlr_xdg_surface: *c.wlr_xdg_surface) void { 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); node.view.init(self, wlr_xdg_surface, self.current_focused_tags);
self.views.push(node); self.views.push(node);
} }
@ -129,7 +130,7 @@ pub const Output = struct {
const visible_count = blk: { const visible_count = blk: {
var count: u32 = 0; 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; while (it.next() != null) count += 1;
break :blk count; break :blk count;
}; };
@ -157,8 +158,9 @@ pub const Output = struct {
} }
var i: u32 = 0; var i: u32 = 0;
var it = ViewStack.pendingIterator(self.views.first, output_tags); var it = ViewStack(View).pendingIterator(self.views.first, output_tags);
while (it.next()) |view| { while (it.next()) |node| {
const view = &node.view;
if (i < master_count) { if (i < master_count) {
// Add the remainder to the first master to ensure every pixel of height is used // Add the remainder to the first master to ensure every pixel of height is used
const master_height = @divTrunc(layout_height, master_count); const master_height = @divTrunc(layout_height, master_count);

View File

@ -44,8 +44,9 @@ pub fn renderOutput(output: *Output) void {
renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now, ox, oy); 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. // The first view in the list is "on top" so iterate in reverse.
var it = ViewStack.reverseIterator(output.views.last, output.current_focused_tags); var it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags);
while (it.next()) |view| { while (it.next()) |node| {
const view = &node.view;
// This check prevents a race condition when a frame is requested // This check prevents a race condition when a frame is requested
// between mapping of a view and the first configure being handled. // between mapping of a view and the first configure being handled.
if (view.current_box.width == 0 or view.current_box.height == 0) { if (view.current_box.width == 0 or view.current_box.height == 0) {

View File

@ -71,10 +71,10 @@ pub const Root = struct {
var output_it = self.outputs.first; var output_it = self.outputs.first;
while (output_it) |node| : (output_it = node.next) { while (output_it) |node| : (output_it = node.next) {
const output = &node.data; const output = &node.data;
var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF);
while (view_it.next()) |view| { while (view_it.next()) |view_node| {
if (view.isAt(lx, ly, surface, sx, sy)) { if (view_node.view.isAt(lx, ly, surface, sx, sy)) {
return view; return &view_node.view;
} }
} }
} }
@ -95,22 +95,22 @@ pub const Root = struct {
const output = self.focusedOutput(); const output = self.focusedOutput();
if (self.focused_view) |current_focus| { if (self.focused_view) |current_focus| {
// If there is a currently focused view, focus the next visible view in the stack. // If there is a currently focused view, focus the next visible view in the stack.
const current_node = @fieldParentPtr(ViewStack.Node, "view", current_focus); const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
var it = ViewStack.iterator(current_node, output.current_focused_tags); var it = ViewStack(View).iterator(current_node, output.current_focused_tags);
// Skip past the current node // Skip past the current node
_ = it.next(); _ = it.next();
// Focus the next visible node if there is one // Focus the next visible node if there is one
if (it.next()) |view| { if (it.next()) |node| {
view.focus(view.wlr_xdg_surface.surface); node.view.focus(node.view.wlr_xdg_surface.surface);
return; return;
} }
} }
// There is either no currently focused view or the last visible view in the // There is either no currently focused view or the last visible view in the
// stack is focused and we need to wrap. // stack is focused and we need to wrap.
var it = ViewStack.iterator(output.views.first, output.current_focused_tags); var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags);
if (it.next()) |view| { if (it.next()) |node| {
view.focus(view.wlr_xdg_surface.surface); node.view.focus(node.view.wlr_xdg_surface.surface);
} else { } else {
// Otherwise clear the focus since there are no visible views // Otherwise clear the focus since there are no visible views
self.clearFocus(); self.clearFocus();
@ -123,22 +123,22 @@ pub const Root = struct {
const output = self.focusedOutput(); const output = self.focusedOutput();
if (self.focused_view) |current_focus| { if (self.focused_view) |current_focus| {
// If there is a currently focused view, focus the previous visible view in the stack. // If there is a currently focused view, focus the previous visible view in the stack.
const current_node = @fieldParentPtr(ViewStack.Node, "view", current_focus); const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
var it = ViewStack.reverseIterator(current_node, output.current_focused_tags); var it = ViewStack(View).reverseIterator(current_node, output.current_focused_tags);
// Skip past the current node // Skip past the current node
_ = it.next(); _ = it.next();
// Focus the previous visible node if there is one // Focus the previous visible node if there is one
if (it.next()) |view| { if (it.next()) |node| {
view.focus(view.wlr_xdg_surface.surface); node.view.focus(node.view.wlr_xdg_surface.surface);
return; return;
} }
} }
// There is either no currently focused view or the first visible view in the // There is either no currently focused view or the first visible view in the
// stack is focused and we need to wrap. // stack is focused and we need to wrap.
var it = ViewStack.reverseIterator(output.views.last, output.current_focused_tags); var it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags);
if (it.next()) |view| { if (it.next()) |node| {
view.focus(view.wlr_xdg_surface.surface); node.view.focus(node.view.wlr_xdg_surface.surface);
} else { } else {
// Otherwise clear the focus since there are no visible views // Otherwise clear the focus since there are no visible views
self.clearFocus(); self.clearFocus();
@ -166,8 +166,9 @@ pub const Root = struct {
var output_it = self.outputs.first; var output_it = self.outputs.first;
while (output_it) |node| : (output_it = node.next) { while (output_it) |node| : (output_it = node.next) {
const output = &node.data; const output = &node.data;
var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF);
while (view_it.next()) |view| { while (view_it.next()) |view_node| {
const view = &view_node.view;
// Clear the serial in case this transaction is interrupting a prior one. // Clear the serial in case this transaction is interrupting a prior one.
view.pending_serial = null; view.pending_serial = null;
@ -259,8 +260,9 @@ pub const Root = struct {
self.focusNextView(); self.focusNextView();
} }
var view_it = ViewStack.iterator(output.views.first, 0xFFFFFFFF); var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF);
while (view_it.next()) |view| { while (view_it.next()) |view_node| {
const view = &view_node.view;
// Ensure that all pending state is cleared // Ensure that all pending state is cleared
view.pending_serial = null; view.pending_serial = null;
if (view.pending_box) |state| { if (view.pending_box) |state| {

View File

@ -156,7 +156,7 @@ pub const View = struct {
const view = @fieldParentPtr(View, "listen_destroy", listener.?); const view = @fieldParentPtr(View, "listen_destroy", listener.?);
const output = view.output; const output = view.output;
const node = @fieldParentPtr(ViewStack.Node, "view", view); const node = @fieldParentPtr(ViewStack(View).Node, "view", view);
output.views.remove(node); output.views.remove(node);
output.root.server.allocator.destroy(node); output.root.server.allocator.destroy(node);
} }

View File

@ -1,8 +1,12 @@
const View = @import("view.zig").View; const View = @import("view.zig").View;
/// A specialized doubly-linked stack that allows for filtered iteration /// A specialized doubly-linked stack that allows for filtered iteration
/// over the nodes /// over the nodes. T must be View or *View.
pub const ViewStack = struct { 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(); const Self = @This();
pub const Node = struct { pub const Node = struct {
@ -11,7 +15,7 @@ pub const ViewStack = struct {
next: ?*Node, next: ?*Node,
/// The view stored in this node /// The view stored in this node
view: View, view: T,
}; };
/// Top/bottom nodes in the stack /// Top/bottom nodes in the stack
@ -69,7 +73,7 @@ pub const ViewStack = struct {
/// Returns the next node in iteration order, or null if done. /// Returns the next node in iteration order, or null if done.
/// This function is horribly ugly, but it's well tested below. /// This function is horribly ugly, but it's well tested below.
pub fn next(self: *Iterator) ?*View { pub fn next(self: *Iterator) ?*Node {
while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { 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.mapped and if (self.pending)
if (node.view.pending_tags) |pending_tags| if (node.view.pending_tags) |pending_tags|
@ -78,9 +82,8 @@ pub const ViewStack = struct {
self.tags & node.view.current_tags != 0 self.tags & node.view.current_tags != 0
else else
self.tags & node.view.current_tags != 0) { self.tags & node.view.current_tags != 0) {
const ret = &node.view;
self.it = if (self.reverse) node.prev else node.next; self.it = if (self.reverse) node.prev else node.next;
return ret; return node;
} }
} }
return null; return null;
@ -124,24 +127,25 @@ pub const ViewStack = struct {
}; };
} }
}; };
}
const testing = @import("std").testing; const testing = @import("std").testing;
test "push/remove" { test "push/remove (*View)" {
const allocator = testing.allocator; const allocator = testing.allocator;
var views: ViewStack = undefined; var views: ViewStack(*View) = undefined;
views.init(); views.init();
const one = try allocator.create(ViewStack.Node); const one = try allocator.create(ViewStack(*View).Node);
defer allocator.destroy(one); defer allocator.destroy(one);
const two = try allocator.create(ViewStack.Node); const two = try allocator.create(ViewStack(*View).Node);
defer allocator.destroy(two); defer allocator.destroy(two);
const three = try allocator.create(ViewStack.Node); const three = try allocator.create(ViewStack(*View).Node);
defer allocator.destroy(three); defer allocator.destroy(three);
const four = try allocator.create(ViewStack.Node); const four = try allocator.create(ViewStack(*View).Node);
defer allocator.destroy(four); defer allocator.destroy(four);
const five = try allocator.create(ViewStack.Node); const five = try allocator.create(ViewStack(*View).Node);
defer allocator.destroy(five); defer allocator.destroy(five);
views.push(three); // {3} views.push(three); // {3}
@ -255,40 +259,40 @@ test "push/remove" {
testing.expect(views.last == null); testing.expect(views.last == null);
} }
test "iteration" { test "iteration (View)" {
const allocator = testing.allocator; const allocator = testing.allocator;
var views: ViewStack = undefined; var views: ViewStack(View) = undefined;
views.init(); 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); defer allocator.destroy(one_a_pb);
one_a_pb.view.mapped = true; one_a_pb.view.mapped = true;
one_a_pb.view.current_tags = 1 << 0; one_a_pb.view.current_tags = 1 << 0;
one_a_pb.view.pending_tags = 1 << 1; 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); defer allocator.destroy(two_a);
two_a.view.mapped = true; two_a.view.mapped = true;
two_a.view.current_tags = 1 << 0; 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); defer allocator.destroy(three_b_pa);
three_b_pa.view.mapped = true; three_b_pa.view.mapped = true;
three_b_pa.view.current_tags = 1 << 1; three_b_pa.view.current_tags = 1 << 1;
three_b_pa.view.pending_tags = 1 << 0; 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); defer allocator.destroy(four_b);
four_b.view.mapped = true; four_b.view.mapped = true;
four_b.view.current_tags = 1 << 1; 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); defer allocator.destroy(five_b);
five_b.view.mapped = true; five_b.view.mapped = true;
five_b.view.current_tags = 1 << 1; 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); defer allocator.destroy(unmapped_1);
unmapped_1.view.mapped = false; unmapped_1.view.mapped = false;
@ -301,92 +305,92 @@ test "iteration" {
// Iteration over all tags // Iteration over all tags
{ {
var it = ViewStack.iterator(views.first, 0xFFFFFFFF); var it = ViewStack(View).iterator(views.first, 0xFFFFFFFF);
testing.expect(it.next() == &two_a.view); testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view);
testing.expect(it.next() == &five_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view);
testing.expect(it.next() == &four_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view);
testing.expect(it.next() == &one_a_pb.view); testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view);
testing.expect(it.next() == &three_b_pa.view); testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Iteration over 'a' tags // Iteration over 'a' tags
{ {
var it = ViewStack.iterator(views.first, 1 << 0); var it = ViewStack(View).iterator(views.first, 1 << 0);
testing.expect(it.next() == &two_a.view); testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view);
testing.expect(it.next() == &one_a_pb.view); testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Iteration over 'b' tags // Iteration over 'b' tags
{ {
var it = ViewStack.iterator(views.first, 1 << 1); var it = ViewStack(View).iterator(views.first, 1 << 1);
testing.expect(it.next() == &five_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view);
testing.expect(it.next() == &four_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view);
testing.expect(it.next() == &three_b_pa.view); testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Iteration over tags that aren't present // 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); testing.expect(it.next() == null);
} }
// Reverse iteration over all tags // Reverse iteration over all tags
{ {
var it = ViewStack.reverseIterator(views.last, 0xFFFFFFFF); var it = ViewStack(View).reverseIterator(views.last, 0xFFFFFFFF);
testing.expect(it.next() == &three_b_pa.view); testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view);
testing.expect(it.next() == &one_a_pb.view); testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view);
testing.expect(it.next() == &four_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view);
testing.expect(it.next() == &five_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view);
testing.expect(it.next() == &two_a.view); testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Reverse iteration over 'a' tags // Reverse iteration over 'a' tags
{ {
var it = ViewStack.reverseIterator(views.last, 1 << 0); var it = ViewStack(View).reverseIterator(views.last, 1 << 0);
testing.expect(it.next() == &one_a_pb.view); testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view);
testing.expect(it.next() == &two_a.view); testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Reverse iteration over 'b' tags // Reverse iteration over 'b' tags
{ {
var it = ViewStack.reverseIterator(views.last, 1 << 1); var it = ViewStack(View).reverseIterator(views.last, 1 << 1);
testing.expect(it.next() == &three_b_pa.view); testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view);
testing.expect(it.next() == &four_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view);
testing.expect(it.next() == &five_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Reverse iteration over tags that aren't present // 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); testing.expect(it.next() == null);
} }
// Iteration over (pending) 'a' tags // Iteration over (pending) 'a' tags
{ {
var it = ViewStack.pendingIterator(views.first, 1 << 0); var it = ViewStack(View).pendingIterator(views.first, 1 << 0);
testing.expect(it.next() == &two_a.view); testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view);
testing.expect(it.next() == &three_b_pa.view); testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Iteration over (pending) 'b' tags // Iteration over (pending) 'b' tags
{ {
var it = ViewStack.pendingIterator(views.first, 1 << 1); var it = ViewStack(View).pendingIterator(views.first, 1 << 1);
testing.expect(it.next() == &five_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view);
testing.expect(it.next() == &four_b.view); testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view);
testing.expect(it.next() == &one_a_pb.view); testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view);
testing.expect(it.next() == null); testing.expect(it.next() == null);
} }
// Iteration over (pending) tags that aren't present // 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); testing.expect(it.next() == null);
} }
} }