output: introduce state struct

This simplifies the handling of the current/pending tags and will be
used in the future for atomic layout updates involving layer surface
exclusive zones.
This commit is contained in:
Isaac Freund 2020-07-02 21:55:21 +02:00
parent 3b508688ea
commit 86386e84bc
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11
11 changed files with 42 additions and 41 deletions

View file

@ -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;
}

View file

@ -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);
};
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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;