Allow storing *View in ViewStack
This is done in preparation for implementing focus stacks.
This commit is contained in:
parent
0f52f664f2
commit
b822084f39
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
46
src/root.zig
46
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| {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
const View = @import("view.zig").View;
|
||||
|
||||
/// A specialized doubly-linked stack that allows for filtered iteration
|
||||
/// over the nodes
|
||||
pub const ViewStack = struct {
|
||||
/// 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();
|
||||
|
||||
pub const Node = struct {
|
||||
@ -11,7 +15,7 @@ pub const ViewStack = struct {
|
||||
next: ?*Node,
|
||||
|
||||
/// The view stored in this node
|
||||
view: View,
|
||||
view: T,
|
||||
};
|
||||
|
||||
/// 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.
|
||||
/// 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) {
|
||||
if (node.view.mapped and if (self.pending)
|
||||
if (node.view.pending_tags) |pending_tags|
|
||||
@ -78,9 +82,8 @@ pub const ViewStack = struct {
|
||||
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;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -123,25 +126,26 @@ pub const ViewStack = struct {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user