diff options
| author | Isaac Freund <mail@isaacfreund.com> | 2023-02-28 17:45:08 +0100 |
|---|---|---|
| committer | Isaac Freund <mail@isaacfreund.com> | 2023-02-28 18:28:17 +0100 |
| commit | 005bde367c839ed981997d552e6beeb68ee0f18b (patch) | |
| tree | 0c99ba10efbb94edcc462e1a4267c464c5ca412f | |
| parent | be4330288d199e1d2c8024c051eb6c08f58a87db (diff) | |
| download | river-005bde367c839ed981997d552e6beeb68ee0f18b.tar.gz river-005bde367c839ed981997d552e6beeb68ee0f18b.tar.xz | |
OutputStatus: rework implementation
This was motivated by the view tags not being proplerly updated by
Root.commitTransaction() when there were no views on an output.
| -rw-r--r-- | river/Layout.zig | 14 | ||||
| -rw-r--r-- | river/Output.zig | 69 | ||||
| -rw-r--r-- | river/OutputStatus.zig | 161 | ||||
| -rw-r--r-- | river/Root.zig | 19 | ||||
| -rw-r--r-- | river/StatusManager.zig | 12 |
5 files changed, 147 insertions, 128 deletions
diff --git a/river/Layout.zig b/river/Layout.zig index b6361ea..351c116 100644 --- a/river/Layout.zig +++ b/river/Layout.zig @@ -159,11 +159,13 @@ fn handleRequest(layout: *river.LayoutV3, request: river.LayoutV3.Request, self: if (layout_demand.serial == req.serial) layout_demand.apply(self); } - if (self.output.layout_name) |name| { - util.gpa.free(name); - } - self.output.layout_name = util.gpa.dupeZ(u8, mem.span(req.layout_name)) catch null; - self.output.sendLayoutName(); + const new_name = util.gpa.dupeZ(u8, mem.sliceTo(req.layout_name, 0)) catch { + log.err("out of memory", .{}); + return; + }; + if (self.output.layout_name) |name| util.gpa.free(name); + self.output.layout_name = new_name; + self.output.status.sendLayoutName(self.output); }, } } @@ -194,7 +196,7 @@ pub fn destroy(self: *Self) void { if (self.output.layout_name) |name| { util.gpa.free(name); self.output.layout_name = null; - self.output.sendLayoutNameClear(); + self.output.status.sendLayoutNameClear(self.output); } } diff --git a/river/Output.zig b/river/Output.zig index 93b5a26..0a921cf 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -171,8 +171,7 @@ layout_name: ?[:0]const u8 = null, /// affect already floating views. layout: ?*Layout = null, -/// List of status tracking objects relaying changes to this output to clients. -status_trackers: std.SinglyLinkedList(OutputStatus) = .{}, +status: OutputStatus, destroy: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleDestroy), enable: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleEnable), @@ -183,7 +182,7 @@ present: wl.Listener(*wlr.Output.event.Present) = wl.Listener(*wlr.Output.event. pub fn create(wlr_output: *wlr.Output) !void { const node = try util.gpa.create(std.TailQueue(Self).Node); errdefer util.gpa.destroy(node); - const self = &node.data; + const output = &node.data; if (!wlr_output.initRender(server.allocator, server.renderer)) return error.InitRenderFailed; @@ -211,7 +210,7 @@ pub fn create(wlr_output: *wlr.Output) !void { const tree = try server.root.layers.outputs.createSceneTree(); const normal_content = try tree.createSceneTree(); - self.* = .{ + output.* = .{ .wlr_output = wlr_output, .tree = tree, .normal_content = normal_content, @@ -244,22 +243,25 @@ pub fn create(wlr_output: *wlr.Output) !void { .width = width, .height = height, }, + .status = undefined, }; - wlr_output.data = @ptrToInt(self); + wlr_output.data = @ptrToInt(output); - self.pending.focus_stack.init(); - self.pending.wm_stack.init(); - self.inflight.focus_stack.init(); - self.inflight.wm_stack.init(); + output.pending.focus_stack.init(); + output.pending.wm_stack.init(); + output.inflight.focus_stack.init(); + output.inflight.wm_stack.init(); - _ = try self.layers.fullscreen.createSceneRect(width, height, &[_]f32{ 0, 0, 0, 1.0 }); - self.layers.fullscreen.node.setEnabled(false); + output.status.init(); - wlr_output.events.destroy.add(&self.destroy); - wlr_output.events.enable.add(&self.enable); - wlr_output.events.mode.add(&self.mode); - wlr_output.events.frame.add(&self.frame); - wlr_output.events.present.add(&self.present); + _ = try output.layers.fullscreen.createSceneRect(width, height, &[_]f32{ 0, 0, 0, 1.0 }); + output.layers.fullscreen.node.setEnabled(false); + + wlr_output.events.destroy.add(&output.destroy); + wlr_output.events.enable.add(&output.enable); + wlr_output.events.mode.add(&output.mode); + wlr_output.events.frame.add(&output.frame); + wlr_output.events.present.add(&output.present); // Ensure that a cursor image at the output's scale factor is loaded // for each seat. @@ -270,13 +272,13 @@ pub fn create(wlr_output: *wlr.Output) !void { std.log.scoped(.cursor).err("failed to load xcursor theme at scale {}", .{wlr_output.scale}); } - self.setTitle(); + output.setTitle(); const ptr_node = try util.gpa.create(std.TailQueue(*Self).Node); ptr_node.data = &node.data; server.root.all_outputs.append(ptr_node); - handleEnable(&self.enable, self.wlr_output); + handleEnable(&output.enable, wlr_output); } pub fn layerSurfaceTree(self: Self, layer: zwlr.LayerShellV1.Layer) *wlr.SceneTree { @@ -289,37 +291,6 @@ pub fn layerSurfaceTree(self: Self, layer: zwlr.LayerShellV1.Layer) *wlr.SceneTr return trees[@intCast(usize, @enumToInt(layer))]; } -pub fn sendViewTags(self: Self) void { - var it = self.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendViewTags(); -} - -pub fn sendUrgentTags(output: *Self) void { - var urgent_tags: u32 = 0; - { - var it = output.inflight.wm_stack.iterator(.forward); - while (it.next()) |view| { - if (view.current.urgent) urgent_tags |= view.current.tags; - } - } - { - var it = output.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendUrgentTags(urgent_tags); - } -} - -pub fn sendLayoutName(self: Self) void { - std.debug.assert(self.layout_name != null); - var it = self.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendLayoutName(self.layout_name.?); -} - -pub fn sendLayoutNameClear(self: Self) void { - std.debug.assert(self.layout_name == null); - var it = self.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendLayoutNameClear(); -} - /// Arrange all layer surfaces of this output and adjust the usable area. /// Will arrange views as well if the usable area changes. pub fn arrangeLayers(self: *Self) void { diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig index ab39458..7e05cf8 100644 --- a/river/OutputStatus.zig +++ b/river/OutputStatus.zig @@ -14,9 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -const Self = @This(); +const OutputStatus = @This(); const std = @import("std"); +const assert = std.debug.assert; + const wayland = @import("wayland"); const wl = wayland.server.wl; const zriver = wayland.server.zriver; @@ -28,80 +30,143 @@ const View = @import("View.zig"); const log = std.log.scoped(.river_status); -output: *Output, -output_status: *zriver.OutputStatusV1, - -pub fn init(self: *Self, output: *Output, output_status: *zriver.OutputStatusV1) void { - self.* = .{ .output = output, .output_status = output_status }; - - output_status.setHandler(*Self, handleRequest, handleDestroy, self); - - // Send view/focused/urgent tags once on bind. - self.sendViewTags(); - self.sendFocusedTags(output.current.tags); +resources: wl.list.Head(zriver.OutputStatusV1, null), +view_tags: std.ArrayListUnmanaged(u32) = .{}, +focused_tags: u32 = 0, +urgent_tags: u32 = 0, - output.sendUrgentTags(); +pub fn init(status: *OutputStatus) void { + status.* = .{ + .resources = undefined, + }; + status.resources.init(); +} - if (output.layout_name) |name| { - self.sendLayoutName(name); +pub fn add(status: *OutputStatus, resource: *zriver.OutputStatusV1, output: *Output) void { + resource.setHandler(?*anyopaque, handleRequest, handleDestroy, null); + + var wl_array: wl.Array = .{ + .size = status.view_tags.items.len * @sizeOf(u32), + .alloc = status.view_tags.items.len * @sizeOf(u32), + .data = status.view_tags.items.ptr, + }; + resource.sendViewTags(&wl_array); + resource.sendFocusedTags(status.focused_tags); + if (resource.getVersion() >= 2) resource.sendUrgentTags(status.urgent_tags); + if (resource.getVersion() >= 4) { + if (output.layout_name) |name| resource.sendLayoutName(name); } + + status.resources.append(resource); } -pub fn destroy(self: *Self) void { - const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self); - self.output.status_trackers.remove(node); - self.output_status.setHandler(*Self, handleRequest, null, self); - util.gpa.destroy(node); +pub fn deinit(status: *OutputStatus) void { + { + var it = status.resources.safeIterator(.forward); + while (it.next()) |resource| { + resource.setHandler(?*anyopaque, handleRequest, null, null); + resource.getLink().remove(); + } + } + status.view_tags.deinit(util.gpa); } -fn handleRequest(output_status: *zriver.OutputStatusV1, request: zriver.OutputStatusV1.Request, _: *Self) void { +fn handleRequest(resource: *zriver.OutputStatusV1, request: zriver.OutputStatusV1.Request, _: ?*anyopaque) void { switch (request) { - .destroy => output_status.destroy(), + .destroy => resource.destroy(), } } -fn handleDestroy(_: *zriver.OutputStatusV1, self: *Self) void { - self.destroy(); +fn handleDestroy(resource: *zriver.OutputStatusV1, _: ?*anyopaque) void { + resource.getLink().remove(); } -/// Send the current tags of each view on the output to the client. -pub fn sendViewTags(self: Self) void { - var view_tags = std.ArrayList(u32).init(util.gpa); - defer view_tags.deinit(); +pub fn handleTransactionCommit(status: *OutputStatus, output: *Output) void { + status.sendViewTags(output); + status.sendFocusedTags(output); + status.sendUrgentTags(output); +} +fn sendViewTags(status: *OutputStatus, output: *Output) void { + var dirty: bool = false; { - var it = self.output.inflight.wm_stack.iterator(.forward); - while (it.next()) |view| { - view_tags.append(view.current.tags) catch { - self.output_status.postNoMemory(); - log.err("out of memory", .{}); - return; - }; + var it = output.inflight.wm_stack.iterator(.forward); + var i: usize = 0; + while (it.next()) |view| : (i += 1) { + assert(view.inflight.tags == view.current.tags); + if (status.view_tags.items.len <= i) { + dirty = true; + _ = status.view_tags.addOne(util.gpa) catch { + log.err("out of memory", .{}); + return; + }; + } else if (view.inflight.tags != status.view_tags.items[i]) { + dirty = true; + } + status.view_tags.items[i] = view.inflight.tags; + } + + if (i != status.view_tags.items.len) { + assert(i < status.view_tags.items.len); + status.view_tags.items.len = i; + dirty = true; } } - var wl_array = wl.Array.fromArrayList(u32, view_tags); - self.output_status.sendViewTags(&wl_array); + if (dirty) { + var wl_array: wl.Array = .{ + .size = status.view_tags.items.len * @sizeOf(u32), + .alloc = status.view_tags.items.len * @sizeOf(u32), + .data = status.view_tags.items.ptr, + }; + var it = status.resources.iterator(.forward); + while (it.next()) |resource| resource.sendViewTags(&wl_array); + } } -pub fn sendFocusedTags(self: Self, tags: u32) void { - self.output_status.sendFocusedTags(tags); +fn sendFocusedTags(status: *OutputStatus, output: *Output) void { + assert(output.inflight.tags == output.current.tags); + if (status.focused_tags != output.inflight.tags) { + status.focused_tags = output.inflight.tags; + + var it = status.resources.iterator(.forward); + while (it.next()) |resource| resource.sendFocusedTags(status.focused_tags); + } } -pub fn sendUrgentTags(self: Self, tags: u32) void { - if (self.output_status.getVersion() >= 2) { - self.output_status.sendUrgentTags(tags); +fn sendUrgentTags(status: *OutputStatus, output: *Output) void { + var urgent_tags: u32 = 0; + { + var it = output.inflight.wm_stack.iterator(.forward); + while (it.next()) |view| { + if (view.current.urgent) urgent_tags |= view.current.tags; + } + } + + if (status.urgent_tags != urgent_tags) { + status.urgent_tags = urgent_tags; + + var it = status.resources.iterator(.forward); + while (it.next()) |resource| { + if (resource.getVersion() >= 2) resource.sendUrgentTags(urgent_tags); + } } } -pub fn sendLayoutName(self: Self, name: [:0]const u8) void { - if (self.output_status.getVersion() >= 4) { - self.output_status.sendLayoutName(name); +pub fn sendLayoutName(status: *OutputStatus, output: *Output) void { + assert(output.layout_name != null); + + var it = status.resources.iterator(.forward); + while (it.next()) |resource| { + if (resource.getVersion() >= 4) resource.sendLayoutName(output.layout_name.?); } } -pub fn sendLayoutNameClear(self: Self) void { - if (self.output_status.getVersion() >= 4) { - self.output_status.sendLayoutNameClear(); +pub fn sendLayoutNameClear(status: *OutputStatus, output: *Output) void { + assert(output.layout_name == null); + + var it = status.resources.iterator(.forward); + while (it.next()) |resource| { + if (resource.getVersion() >= 4) resource.sendLayoutNameClear(); } } diff --git a/river/Root.zig b/river/Root.zig index 819397c..747f9e6 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -298,7 +298,7 @@ pub fn removeOutput(root: *Self, output: *Output) void { } } - while (output.status_trackers.first) |status_node| status_node.data.destroy(); + output.status.deinit(); root.applyPending(); } @@ -567,12 +567,8 @@ fn commitTransaction(root: *Self) void { "changing current focus: {b:0>10} to {b:0>10}", .{ output.current.tags, output.inflight.tags }, ); - - output.current.tags = output.pending.tags; - - var it = output.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendFocusedTags(output.current.tags); } + output.current.tags = output.inflight.tags; if (output.inflight.fullscreen != output.current.fullscreen) { if (output.current.fullscreen) |view| { @@ -590,23 +586,17 @@ fn commitTransaction(root: *Self) void { output.layers.fullscreen.node.setEnabled(output.current.fullscreen != null); } - var view_tags_changed = false; - var urgent_tags_dirty = false; - var focus_stack_it = output.inflight.focus_stack.iterator(.forward); while (focus_stack_it.next()) |view| { assert(view.inflight.output == output); view.inflight_serial = null; - if (view.inflight.tags != view.current.tags) view_tags_changed = true; - if (view.inflight.urgent != view.current.urgent) urgent_tags_dirty = true; - if (view.inflight.urgent and view_tags_changed) urgent_tags_dirty = true; - if (view.current.output != output) { view.tree.node.reparent(output.layers.views); view.popup_tree.node.reparent(output.layers.popups); } + const enabled = view.current.tags & output.current.tags != 0; view.tree.node.setEnabled(enabled); view.popup_tree.node.setEnabled(enabled); @@ -616,8 +606,7 @@ fn commitTransaction(root: *Self) void { view.updateCurrent(); } - if (view_tags_changed) output.sendViewTags(); - if (urgent_tags_dirty) output.sendUrgentTags(); + output.status.handleTransactionCommit(output); } { diff --git a/river/StatusManager.zig b/river/StatusManager.zig index 3a957d1..60c5d59 100644 --- a/river/StatusManager.zig +++ b/river/StatusManager.zig @@ -71,25 +71,17 @@ fn handleRequest( const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return; const output = @intToPtr(*Output, wlr_output.data); - const node = util.gpa.create(std.SinglyLinkedList(OutputStatus).Node) catch { - status_manager.getClient().postNoMemory(); - log.err("out of memory", .{}); - return; - }; - - const output_status = zriver.OutputStatusV1.create( + const resource = zriver.OutputStatusV1.create( status_manager.getClient(), status_manager.getVersion(), req.id, ) catch { status_manager.getClient().postNoMemory(); - util.gpa.destroy(node); log.err("out of memory", .{}); return; }; - node.data.init(output, output_status); - output.status_trackers.prepend(node); + output.status.add(resource, output); }, .get_river_seat_status => |req| { // ignore if the seat is inert |
