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:
parent
3b508688ea
commit
86386e84bc
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user