diff options
| author | Isaac Freund <mail@isaacfreund.com> | 2023-02-22 23:21:28 +0100 |
|---|---|---|
| committer | Isaac Freund <mail@isaacfreund.com> | 2023-02-28 14:55:59 +0100 |
| commit | f5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c (patch) | |
| tree | 1048b2b9e607855481bcd7ee89658af3e22ff078 | |
| parent | ce7fda4ed943be2dcdfe87e4a0456348f2fe7433 (diff) | |
| download | river-f5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c.tar.gz river-f5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c.tar.xz | |
View: use saved surface trees for transactions
| -rw-r--r-- | river/Root.zig | 8 | ||||
| -rw-r--r-- | river/View.zig | 107 | ||||
| -rw-r--r-- | river/XdgToplevel.zig | 46 | ||||
| -rw-r--r-- | river/XwaylandView.zig | 33 |
4 files changed, 86 insertions, 108 deletions
diff --git a/river/Root.zig b/river/Root.zig index 1a57426..2e7d705 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -393,9 +393,9 @@ pub fn startTransaction(self: *Self) void { view.sendFrameDone(); } - // If there are saved buffers present, then this transaction is interrupting - // a previous transaction and we should keep the old buffers. - if (view.saved_buffers.items.len == 0) view.saveBuffers(); + // If the saved surface tree is enabled, then this transaction is interrupting + // a previous transaction and we should keep the old surface tree. + if (!view.saved_surface_tree.node.enabled) view.saveSurfaceTree(); } else { if (view.needsConfigure()) view.configure(); } @@ -473,7 +473,7 @@ fn commitTransaction(self: *Self) void { view_it = view_node.next; if (!view.tree.node.enabled) { - view.dropSavedBuffers(); + view.dropSavedSurfaceTree(); view.output.views.remove(view_node); if (view.destroying) view.destroy(); continue; diff --git a/river/View.zig b/river/View.zig index 8cd96fe..8fb07cd 100644 --- a/river/View.zig +++ b/river/View.zig @@ -63,14 +63,6 @@ const State = struct { urgent: bool = false, }; -const SavedBuffer = struct { - client_buffer: *wlr.ClientBuffer, - /// x/y relative to the root surface in the surface tree. - surface_box: wlr.Box, - source_box: wlr.FBox, - transform: wl.Output.Transform, -}; - /// The implementation of this view impl: Impl, @@ -78,6 +70,8 @@ impl: Impl, output: *Output, tree: *wlr.SceneTree, +surface_tree: *wlr.SceneTree, +saved_surface_tree: *wlr.SceneTree, borders: struct { left: *wlr.SceneRect, right: *wlr.SceneRect, @@ -97,17 +91,6 @@ pending: State, /// The serial sent with the currently pending configure event pending_serial: ?u32 = null, -/// The currently commited geometry of the surface. The x/y may be negative if -/// for example the client has decided to draw CSD shadows a la GTK. -surface_box: wlr.Box = undefined, - -/// The geometry the view's surface had when the transaction started and -/// buffers were saved. -saved_surface_box: wlr.Box = undefined, - -/// These are what we render while a transaction is in progress -saved_buffers: std.ArrayListUnmanaged(SavedBuffer) = .{}, - /// The floating dimensions the view, saved so that they can be restored if the /// view returns to floating mode. float_box: wlr.Box = undefined, @@ -121,29 +104,45 @@ draw_borders: bool = true, request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) = wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate), -pub fn init(self: *Self, output: *Output, tree: *wlr.SceneTree, impl: Impl) error{OutOfMemory}!void { +pub fn create(output: *Output, impl: Impl) error{OutOfMemory}!*Self { + const node = try util.gpa.create(ViewStack(Self).Node); + errdefer util.gpa.destroy(node); + const self = &node.view; + const initial_tags = blk: { const tags = output.current.tags & server.config.spawn_tagmask; break :blk if (tags != 0) tags else output.current.tags; }; + const tree = try output.layers.views.createSceneTree(); + errdefer tree.node.destroy(); + + const popup_tree = try output.layers.popups.createSceneTree(); + errdefer popup_tree.node.destroy(); + self.* = .{ .impl = impl, .output = output, .tree = tree, + .surface_tree = try tree.createSceneTree(), + .saved_surface_tree = try tree.createSceneTree(), .borders = .{ .left = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused), .right = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused), .top = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused), .bottom = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused), }, - .popup_tree = try output.layers.popups.createSceneTree(), + .popup_tree = popup_tree, .current = .{ .tags = initial_tags }, .pending = .{ .tags = initial_tags }, }; + self.saved_surface_tree.node.setEnabled(false); + try SceneNodeData.attach(&self.tree.node, .{ .view = self }); try SceneNodeData.attach(&self.popup_tree.node, .{ .view = self }); + + return self; } /// If saved buffers of the view are currently in use by a transaction, @@ -155,8 +154,9 @@ pub fn destroy(self: *Self) void { // If there are still saved buffers, then this view needs to be kept // around until the current transaction completes. This function will be // called again in Root.commitTransaction() - if (self.saved_buffers.items.len == 0) { - self.saved_buffers.deinit(util.gpa); + if (!self.saved_surface_tree.node.enabled) { + self.tree.node.destroy(); + self.popup_tree.node.destroy(); const node = @fieldParentPtr(ViewStack(Self).Node, "view", self); util.gpa.destroy(node); @@ -204,7 +204,7 @@ pub fn updateCurrent(self: *Self) void { const config = &server.config; self.current = self.pending; - self.dropSavedBuffers(); + if (self.saved_surface_tree.node.enabled) self.dropSavedSurfaceTree(); const color = blk: { if (self.current.urgent) break :blk &config.border_color_urgent; @@ -274,39 +274,40 @@ pub fn sendFrameDone(self: Self) void { self.rootSurface().sendFrameDone(&now); } -pub fn dropSavedBuffers(self: *Self) void { - for (self.saved_buffers.items) |buffer| buffer.client_buffer.base.unlock(); - self.saved_buffers.items.len = 0; +pub fn dropSavedSurfaceTree(self: *Self) void { + assert(self.saved_surface_tree.node.enabled); + + var it = self.saved_surface_tree.children.safeIterator(.forward); + while (it.next()) |node| node.destroy(); + + self.saved_surface_tree.node.setEnabled(false); + self.surface_tree.node.setEnabled(true); } -pub fn saveBuffers(self: *Self) void { - assert(self.saved_buffers.items.len == 0); - self.saved_surface_box = self.surface_box; - self.forEachSurface(*std.ArrayListUnmanaged(SavedBuffer), saveBuffersIterator, &self.saved_buffers); +pub fn saveSurfaceTree(self: *Self) void { + assert(!self.saved_surface_tree.node.enabled); + assert(self.saved_surface_tree.children.empty()); + + self.surface_tree.node.forEachBuffer(*wlr.SceneTree, saveSurfaceTreeIter, self.saved_surface_tree); + + self.surface_tree.node.setEnabled(false); + self.saved_surface_tree.node.setEnabled(true); } -fn saveBuffersIterator( - surface: *wlr.Surface, - surface_x: c_int, - surface_y: c_int, - saved_buffers: *std.ArrayListUnmanaged(SavedBuffer), +fn saveSurfaceTreeIter( + buffer: *wlr.SceneBuffer, + sx: c_int, + sy: c_int, + saved_surface_tree: *wlr.SceneTree, ) void { - if (surface.buffer) |buffer| { - var source_box: wlr.FBox = undefined; - surface.getBufferSourceBox(&source_box); - saved_buffers.append(util.gpa, .{ - .client_buffer = buffer, - .surface_box = .{ - .x = surface_x, - .y = surface_y, - .width = surface.current.width, - .height = surface.current.height, - }, - .source_box = source_box, - .transform = surface.current.transform, - }) catch return; - _ = buffer.base.lock(); - } + const saved = saved_surface_tree.createSceneBuffer(buffer.buffer) catch { + log.err("out of memory", .{}); + return; + }; + saved.node.setPosition(sx, sy); + saved.setDestSize(buffer.dst_width, buffer.dst_height); + saved.setSourceBox(&buffer.src_box); + saved.setTransform(buffer.transform); } /// Move a view from one output to another, sending the required enter/leave @@ -524,7 +525,7 @@ pub fn unmap(self: *Self) void { self.tree.node.setEnabled(false); self.popup_tree.node.setEnabled(false); - if (self.saved_buffers.items.len == 0) self.saveBuffers(); + if (!self.saved_surface_tree.node.enabled) self.saveSurfaceTree(); // Inform all seats that the view has been unmapped so they can handle focus var it = server.input_manager.seats.first; diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index 73c1efa..a15a3d1 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -32,12 +32,13 @@ const ViewStack = @import("view_stack.zig").ViewStack; const log = std.log.scoped(.xdg_shell); -/// The view this xdg toplevel implements +/// TODO(zig): get rid of this and use @fieldParentPtr(), https://github.com/ziglang/zig/issues/6611 view: *View, -/// The corresponding wlroots object xdg_toplevel: *wlr.XdgToplevel, +geometry: wlr.Box, + /// Set to true when the client acks the configure with serial View.pending_serial. acked_pending_serial: bool = false, @@ -59,19 +60,18 @@ request_resize: wl.Listener(*wlr.XdgToplevel.event.Resize) = set_title: wl.Listener(void) = wl.Listener(void).init(handleSetTitle), set_app_id: wl.Listener(void) = wl.Listener(void).init(handleSetAppId), -/// The View will add itself to the output's view stack on map pub fn create(output: *Output, xdg_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void { - const node = try util.gpa.create(ViewStack(View).Node); - errdefer util.gpa.destroy(node); - const view = &node.view; - - const tree = try output.layers.views.createSceneTree(); - _ = try tree.createSceneXdgSurface(xdg_toplevel.base); - - try view.init(output, tree, .{ .xdg_toplevel = .{ - .view = view, + const view = try View.create(output, .{ .xdg_toplevel = .{ + .view = undefined, .xdg_toplevel = xdg_toplevel, + .geometry = undefined, } }); + errdefer view.destroy(); + + view.impl.xdg_toplevel.view = view; + xdg_toplevel.base.getGeometry(&view.impl.xdg_toplevel.geometry); + + _ = try view.surface_tree.createSceneXdgSurface(xdg_toplevel.base); xdg_toplevel.base.data = @ptrToInt(view); @@ -273,14 +273,14 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { const self = @fieldParentPtr(Self, "commit", listener); const view = self.view; - var new_box: wlr.Box = undefined; - self.xdg_toplevel.base.getGeometry(&new_box); + var new_geometry: wlr.Box = undefined; + self.xdg_toplevel.base.getGeometry(&new_geometry); + + const size_changed = !std.meta.eql(self.geometry, new_geometry); + self.geometry = new_geometry; // If we have sent a configure changing the size if (view.pending_serial != null) { - // Update the stored dimensions of the surface - view.surface_box = new_box; - if (self.acked_pending_serial) { // If this commit is in response to our configure and the // transaction code is tracking this configure, notify it. @@ -307,16 +307,12 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { // stashed buffer from when the transaction started. view.sendFrameDone(); } - } else { - const size_changed = !std.meta.eql(view.surface_box, new_box); - view.surface_box = new_box; + } else if ((self.view.pending.float or self.view.output.pending.layout == null) and size_changed) { // If the client has decided to resize itself and the view is floating, // then respect that resize. - if ((self.view.pending.float or self.view.output.pending.layout == null) and size_changed) { - view.pending.box.width = new_box.width; - view.pending.box.height = new_box.height; - view.applyPending(); - } + view.pending.box.width = new_geometry.width; + view.pending.box.height = new_geometry.height; + view.applyPending(); } } diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index f5de473..e17ac0a 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -33,7 +33,7 @@ const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig"); const log = std.log.scoped(.xwayland); -/// The view this xwayland view implements +/// TODO(zig): get rid of this and use @fieldParentPtr(), https://github.com/ziglang/zig/issues/6611 view: *View, xwayland_surface: *wlr.XwaylandSurface, @@ -55,7 +55,6 @@ set_override_redirect: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleSetOverrideRedirect), // Listeners that are only active while the view is mapped -commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), set_title: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleSetTitle), set_class: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleSetClass), request_fullscreen: wl.Listener(*wlr.XwaylandSurface) = @@ -64,17 +63,14 @@ request_minimize: wl.Listener(*wlr.XwaylandSurface.event.Minimize) = wl.Listener(*wlr.XwaylandSurface.event.Minimize).init(handleRequestMinimize), pub fn create(output: *Output, xwayland_surface: *wlr.XwaylandSurface) error{OutOfMemory}!void { - const node = try util.gpa.create(ViewStack(View).Node); - const view = &node.view; - - const tree = try output.layers.views.createSceneTree(); - - try view.init(output, tree, .{ .xwayland_view = .{ - .view = view, + const view = try View.create(output, .{ .xwayland_view = .{ + .view = undefined, .xwayland_surface = xwayland_surface, } }); + errdefer view.destroy(); - const self = &node.view.impl.xwayland_view; + const self = &view.impl.xwayland_view; + self.view = view; xwayland_surface.data = @ptrToInt(self); // Add listeners that are active over the view's entire lifetime @@ -189,19 +185,16 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: // Add listeners that are only active while mapped const surface = xwayland_surface.surface.?; - surface.events.commit.add(&self.commit); xwayland_surface.events.set_title.add(&self.set_title); xwayland_surface.events.set_class.add(&self.set_class); xwayland_surface.events.request_fullscreen.add(&self.request_fullscreen); xwayland_surface.events.request_minimize.add(&self.request_minimize); - self.surface_tree = view.tree.createSceneSubsurfaceTree(surface) catch { + self.surface_tree = view.surface_tree.createSceneSubsurfaceTree(surface) catch { log.err("out of memory", .{}); surface.resource.getClient().postNoMemory(); return; }; - // Place the node below the view's border nodes - self.surface_tree.?.node.lowerToBottom(); // Use the view's "natural" size centered on the output as the default // floating dimensions @@ -240,7 +233,6 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSur self.surface_tree = null; // Remove listeners that are only active while mapped - self.commit.link.remove(); self.set_title.link.remove(); self.set_class.link.remove(); self.request_fullscreen.link.remove(); @@ -288,17 +280,6 @@ fn handleSetOverrideRedirect( }; } -fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { - const self = @fieldParentPtr(Self, "commit", listener); - - self.view.surface_box = .{ - .x = 0, - .y = 0, - .width = surface.current.width, - .height = surface.current.height, - }; -} - fn handleSetTitle(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void { const self = @fieldParentPtr(Self, "set_title", listener); self.view.notifyTitle(); |
