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.
|
/// 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 {
|
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.
|
// 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| {
|
while (it.next()) |node| {
|
||||||
if (!node.view.focused) continue;
|
if (!node.view.focused) continue;
|
||||||
if (node.view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
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| {
|
while (it.next()) |node| {
|
||||||
if (node.view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
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.
|
// that it should never be encountered during normal usage.
|
||||||
const minimum_size = 50;
|
const minimum_size = 50;
|
||||||
|
|
||||||
|
const State = struct {
|
||||||
|
/// A bit field of focused tags
|
||||||
|
tags: u32,
|
||||||
|
};
|
||||||
|
|
||||||
root: *Root,
|
root: *Root,
|
||||||
wlr_output: *c.wlr_output,
|
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
|
/// The area left for views and other layer surfaces after applying the
|
||||||
/// exclusive zones of exclusive layer surfaces.
|
/// exclusive zones of exclusive layer surfaces.
|
||||||
|
/// TODO: this should be part of the output's State
|
||||||
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(View),
|
views: ViewStack(View),
|
||||||
|
|
||||||
/// A bit field of focused tags
|
/// The double-buffered state of the output.
|
||||||
current_focused_tags: u32,
|
current: State,
|
||||||
pending_focused_tags: ?u32,
|
pending: State,
|
||||||
|
|
||||||
/// Number of views in "master" section of the screen.
|
/// Number of views in "master" section of the screen.
|
||||||
master_count: u32,
|
master_count: u32,
|
||||||
@ -98,8 +104,10 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
|
|||||||
|
|
||||||
self.views.init();
|
self.views.init();
|
||||||
|
|
||||||
self.current_focused_tags = 1 << 0;
|
self.current = .{
|
||||||
self.pending_focused_tags = null;
|
.tags = 1 << 0,
|
||||||
|
};
|
||||||
|
self.pending = self.current;
|
||||||
|
|
||||||
self.master_count = 1;
|
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
|
/// pending state, the changes are not appplied until a transaction is started
|
||||||
/// and completed.
|
/// and completed.
|
||||||
pub fn arrangeViews(self: *Self) void {
|
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).*);
|
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
|
// Make fullscreen views take the full area, count up views that will be
|
||||||
// arranged by the layout.
|
// arranged by the layout.
|
||||||
var layout_count: u32 = 0;
|
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| {
|
while (it.next()) |node| {
|
||||||
const view = &node.view;
|
const view = &node.view;
|
||||||
if (view.pending.fullscreen) {
|
if (view.pending.fullscreen) {
|
||||||
@ -321,9 +324,9 @@ pub fn arrangeViews(self: *Self) void {
|
|||||||
// would cause an underflow and is pointless anyway.
|
// 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 (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) {
|
switch (err) {
|
||||||
LayoutError.BadExitCode => log.err(.layout, "layout command exited with non-zero return code", .{}),
|
LayoutError.BadExitCode => log.err(.layout, "layout command exited with non-zero return code", .{}),
|
||||||
LayoutError.BadWindowConfiguration => log.err(.layout, "invalid window configuration", .{}),
|
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}),
|
else => log.err(.layout, "'{}' error while trying to use external layout", .{err}),
|
||||||
}
|
}
|
||||||
log.err(.layout, "falling back to internal layout", .{});
|
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.
|
/// Send the currently focused tags of the output to the client.
|
||||||
pub fn sendFocusedTags(self: Self) void {
|
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) {
|
while (output_it) |output_node| : (output_it = output_node.next) {
|
||||||
const output = &output_node.data;
|
const output = &output_node.data;
|
||||||
|
|
||||||
// If there were pending focused tags, make them the current focus
|
// Apply pending state of the output
|
||||||
if (output.pending_focused_tags) |tags| {
|
if (output.pending.tags != output.current.tags) {
|
||||||
log.debug(
|
log.debug(
|
||||||
.output,
|
.output,
|
||||||
"changing current focus: {b:0>10} to {b:0>10}",
|
"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;
|
var it = output.status_trackers.first;
|
||||||
while (it) |node| : (it = node.next) node.data.sendFocusedTags();
|
while (it) |node| : (it = node.next) node.data.sendFocusedTags();
|
||||||
}
|
}
|
||||||
|
output.current = output.pending;
|
||||||
|
|
||||||
var view_tags_changed = false;
|
var view_tags_changed = false;
|
||||||
|
|
||||||
var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
|
var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
|
||||||
while (view_it.next()) |view_node| {
|
while (view_it.next()) |view_node| {
|
||||||
const view = &view_node.view;
|
const view = &view_node.view;
|
||||||
// Apply pending state
|
// Apply pending state of the view
|
||||||
view.pending_serial = null;
|
view.pending_serial = null;
|
||||||
if (view.pending.tags != view.current.tags) view_tags_changed = true;
|
if (view.pending.tags != view.current.tags) view_tags_changed = true;
|
||||||
view.current = view.pending;
|
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 the view is not currently visible, behave as if null was passed
|
||||||
if (view) |v| {
|
if (view) |v| {
|
||||||
if (v.output != self.focused_output or
|
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
|
// If the target view is not fullscreen or null, then a fullscreen view
|
||||||
// will grab focus if visible.
|
// will grab focus if visible.
|
||||||
if (if (view) |v| !v.current.fullscreen else true) {
|
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| {
|
view = while (it.next()) |node| {
|
||||||
if (node.view.output == self.focused_output and node.view.current.fullscreen) break node.view;
|
if (node.view.output == self.focused_output and node.view.current.fullscreen) break node.view;
|
||||||
} else view;
|
} else view;
|
||||||
@ -139,7 +139,7 @@ pub fn focus(self: *Self, _view: ?*View) void {
|
|||||||
|
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
// Set view to the first currently visible view in the focus stack if any
|
// 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| {
|
view = while (it.next()) |node| {
|
||||||
if (node.view.output == self.focused_output) break node.view;
|
if (node.view.output == self.focused_output) break node.view;
|
||||||
} else null;
|
} 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
|
// The View will add itself to the output's view stack on map
|
||||||
const output = self.input_manager.default_seat.focused_output;
|
const output = self.input_manager.default_seat.focused_output;
|
||||||
const node = util.gpa.create(ViewStack(View).Node) catch unreachable;
|
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.
|
/// 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
|
// The View will add itself to the output's view stack on map
|
||||||
const output = self.input_manager.default_seat.focused_output;
|
const output = self.input_manager.default_seat.focused_output;
|
||||||
const node = util.gpa.create(ViewStack(View).Node) catch unreachable;
|
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,
|
.float = false,
|
||||||
.fullscreen = false,
|
.fullscreen = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.pending = self.current;
|
self.pending = self.current;
|
||||||
|
|
||||||
self.pending_serial = null;
|
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.
|
// 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);
|
const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
|
||||||
var it = switch (direction) {
|
var it = switch (direction) {
|
||||||
.Next => ViewStack(View).iterator(focused_node, output.current_focused_tags),
|
.Next => ViewStack(View).iterator(focused_node, output.current.tags),
|
||||||
.Prev => ViewStack(View).reverseIterator(focused_node, output.current_focused_tags),
|
.Prev => ViewStack(View).reverseIterator(focused_node, output.current.tags),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip past the focused node
|
// 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
|
// 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 = switch (direction) {
|
var it = switch (direction) {
|
||||||
.Next => ViewStack(View).iterator(output.views.first, output.current_focused_tags),
|
.Next => ViewStack(View).iterator(output.views.first, output.current.tags),
|
||||||
.Prev => ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags),
|
.Prev => ViewStack(View).reverseIterator(output.views.last, output.current.tags),
|
||||||
};
|
};
|
||||||
|
|
||||||
seat.focus(if (it.next()) |node| &node.view else null);
|
seat.focus(if (it.next()) |node| &node.view else null);
|
||||||
|
@ -28,8 +28,8 @@ pub fn setFocusedTags(
|
|||||||
out: *?[]const u8,
|
out: *?[]const u8,
|
||||||
) Error!void {
|
) Error!void {
|
||||||
const tags = try parseTags(allocator, args, out);
|
const tags = try parseTags(allocator, args, out);
|
||||||
if (seat.focused_output.current_focused_tags != tags) {
|
if (seat.focused_output.pending.tags != tags) {
|
||||||
seat.focused_output.pending_focused_tags = tags;
|
seat.focused_output.pending.tags = tags;
|
||||||
seat.input_manager.server.root.arrange();
|
seat.input_manager.server.root.arrange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,9 +57,9 @@ pub fn toggleFocusedTags(
|
|||||||
) Error!void {
|
) Error!void {
|
||||||
const tags = try parseTags(allocator, args, out);
|
const tags = try parseTags(allocator, args, out);
|
||||||
const output = seat.focused_output;
|
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) {
|
if (new_focused_tags != 0) {
|
||||||
output.pending_focused_tags = new_focused_tags;
|
output.pending.tags = new_focused_tags;
|
||||||
seat.input_manager.server.root.arrange();
|
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
|
// 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.
|
// 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| {
|
const layout_first = while (it.next()) |node| {
|
||||||
if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
|
if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
|
||||||
} else unreachable;
|
} else unreachable;
|
||||||
|
@ -57,7 +57,7 @@ pub fn renderOutput(output: *Output) void {
|
|||||||
c.wlr_renderer_begin(wlr_renderer, width, height);
|
c.wlr_renderer_begin(wlr_renderer, width, height);
|
||||||
|
|
||||||
// Find the first visible fullscreen view in the stack if there is one
|
// 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| {
|
const fullscreen_view = while (it.next()) |node| {
|
||||||
if (node.view.current.fullscreen) break &node.view;
|
if (node.view.current.fullscreen) break &node.view;
|
||||||
} else null;
|
} else null;
|
||||||
@ -76,7 +76,7 @@ pub fn renderOutput(output: *Output) void {
|
|||||||
renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now);
|
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.
|
// 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| {
|
while (it.next()) |node| {
|
||||||
const view = &node.view;
|
const view = &node.view;
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ pub fn renderOutput(output: *Output) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render focused views
|
// 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| {
|
while (it.next()) |node| {
|
||||||
const view = &node.view;
|
const view = &node.view;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user