diff options
| -rw-r--r-- | river/Root.zig | 3 | ||||
| -rw-r--r-- | river/View.zig | 43 | ||||
| -rw-r--r-- | river/XdgToplevel.zig | 23 | ||||
| -rw-r--r-- | river/XwaylandView.zig | 17 |
4 files changed, 39 insertions, 47 deletions
diff --git a/river/Root.zig b/river/Root.zig index 7245e58..4467fa5 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -519,6 +519,9 @@ fn sendConfigures(root: *Self) void { var focus_stack_it = output.inflight.focus_stack.iterator(.forward); while (focus_stack_it.next()) |view| { + // This can happen if a view is unmapped while a layout demand including it is inflight + if (!view.mapped) continue; + if (view.needsConfigure()) { view.configure(); diff --git a/river/View.zig b/river/View.zig index af44cdb..9a4fe5b 100644 --- a/river/View.zig +++ b/river/View.zig @@ -36,10 +36,10 @@ const XwaylandView = @import("XwaylandView.zig"); const log = std.log.scoped(.view); pub const Constraints = struct { - min_width: u31, - max_width: u31, - min_height: u31, - max_height: u31, + min_width: u31 = 1, + max_width: u31 = math.maxInt(u31), + min_height: u31 = 1, + max_height: u31 = math.maxInt(u31), }; const Impl = union(enum) { @@ -121,6 +121,10 @@ borders: struct { }, popup_tree: *wlr.SceneTree, +/// Bounds on the width/height of the view, set by the xdg_toplevel/xwayland_view implementation. +constraints: Constraints = .{}, + +mapped: bool = false, /// This indicates that the view should be destroyed when the current /// transaction completes. See View.destroy() destroying: bool = false, @@ -256,6 +260,7 @@ pub fn updateCurrent(view: *Self) void { } pub fn needsConfigure(self: Self) bool { + assert(self.mapped); return switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.needsConfigure(), .xwayland_view => |xwayland_view| xwayland_view.needsConfigure(), @@ -263,6 +268,7 @@ pub fn needsConfigure(self: Self) bool { } pub fn configure(self: *Self) void { + assert(self.mapped and !self.destroying); switch (self.impl) { .xdg_toplevel => |*xdg_toplevel| xdg_toplevel.configure(), .xwayland_view => |*xwayland_view| { @@ -274,7 +280,7 @@ pub fn configure(self: *Self) void { } pub fn rootSurface(self: Self) *wlr.Surface { - assert(!self.destroying); + assert(self.mapped and !self.destroying); return switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.rootSurface(), .xwayland_view => |xwayland_view| xwayland_view.rootSurface(), @@ -282,7 +288,7 @@ pub fn rootSurface(self: Self) *wlr.Surface { } pub fn sendFrameDone(self: Self) void { - assert(!self.destroying); + assert(self.mapped and !self.destroying); var now: os.timespec = undefined; os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported"); self.rootSurface().sendFrameDone(&now); @@ -341,6 +347,7 @@ pub fn setPendingOutput(view: *Self, output: *Output) void { } pub fn close(self: Self) void { + assert(!self.destroying); switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.close(), .xwayland_view => |xwayland_view| xwayland_view.close(), @@ -348,6 +355,7 @@ pub fn close(self: Self) void { } pub fn destroyPopups(self: Self) void { + assert(!self.destroying); switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.destroyPopups(), .xwayland_view => {}, @@ -356,6 +364,7 @@ pub fn destroyPopups(self: Self) void { /// Return the current title of the view if any. pub fn getTitle(self: Self) ?[*:0]const u8 { + assert(!self.destroying); return switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.getTitle(), .xwayland_view => |xwayland_view| xwayland_view.getTitle(), @@ -364,6 +373,7 @@ pub fn getTitle(self: Self) ?[*:0]const u8 { /// Return the current app_id of the view if any. pub fn getAppId(self: Self) ?[*:0]const u8 { + assert(!self.destroying); return switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.getAppId(), .xwayland_view => |xwayland_view| xwayland_view.getAppId(), @@ -372,17 +382,8 @@ pub fn getAppId(self: Self) ?[*:0]const u8 { /// Clamp the width/height of the box to the constraints of the view pub fn applyConstraints(self: *Self, box: *wlr.Box) void { - const constraints = self.getConstraints(); - box.width = math.clamp(box.width, constraints.min_width, constraints.max_width); - box.height = math.clamp(box.height, constraints.min_height, constraints.max_height); -} - -/// Return bounds on the dimensions of the view -pub fn getConstraints(self: Self) Constraints { - return switch (self.impl) { - .xdg_toplevel => |xdg_toplevel| xdg_toplevel.getConstraints(), - .xwayland_view => |xwayland_view| xwayland_view.getConstraints(), - }; + box.width = math.clamp(box.width, self.constraints.min_width, self.constraints.max_width); + box.height = math.clamp(box.height, self.constraints.min_height, self.constraints.max_height); } /// Find and return the view corresponding to a given surface, if any @@ -404,6 +405,11 @@ pub fn fromWlrSurface(surface: *wlr.Surface) ?*Self { pub fn map(view: *Self) !void { log.debug("view '{?s}' mapped", .{view.getTitle()}); + assert(!view.mapped and !view.destroying); + view.mapped = true; + + view.pending.borders = !server.config.csdAllowed(view); + server.xdg_activation.events.request_activate.add(&view.request_activate); if (server.input_manager.defaultSeat().focused_output) |output| { @@ -443,6 +449,9 @@ pub fn unmap(view: *Self) void { view.request_activate.link.remove(); + assert(view.mapped and !view.destroying); + view.mapped = false; + server.root.applyPending(); } diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index cade2eb..47182e5 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -138,17 +138,6 @@ pub fn getAppId(self: Self) ?[*:0]const u8 { return self.xdg_toplevel.app_id; } -/// Return bounds on the dimensions of the toplevel. -pub fn getConstraints(self: Self) View.Constraints { - const state = &self.xdg_toplevel.current; - return .{ - .min_width = @intCast(u31, math.max(state.min_width, 1)), - .max_width = if (state.max_width > 0) @intCast(u31, state.max_width) else math.maxInt(u31), - .min_height = @intCast(u31, math.max(state.min_height, 1)), - .max_height = if (state.max_height > 0) @intCast(u31, state.max_height) else math.maxInt(u31), - }; -} - pub fn destroyPopups(self: Self) void { var it = self.xdg_toplevel.base.popups.safeIterator(.forward); while (it.next()) |wlr_xdg_popup| wlr_xdg_popup.destroy(); @@ -201,8 +190,6 @@ fn handleMap(listener: *wl.Listener(void)) void { self.view.pending.fullscreen = self.xdg_toplevel.requested.fullscreen; - view.pending.borders = !server.config.csdAllowed(view); - view.map() catch { log.err("out of memory", .{}); self.xdg_toplevel.resource.getClient().postNoMemory(); @@ -258,6 +245,16 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { const self = @fieldParentPtr(Self, "commit", listener); const view = self.view; + { + const state = &self.xdg_toplevel.current; + view.constraints = .{ + .min_width = @intCast(u31, math.max(state.min_width, 1)), + .max_width = if (state.max_width > 0) @intCast(u31, state.max_width) else math.maxInt(u31), + .min_height = @intCast(u31, math.max(state.min_height, 1)), + .max_height = if (state.max_height > 0) @intCast(u31, state.max_height) else math.maxInt(u31), + }; + } + var new_geometry: wlr.Box = undefined; self.xdg_toplevel.base.getGeometry(&new_geometry); diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index d94393b..0a7c62a 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -142,23 +142,6 @@ pub fn getAppId(self: Self) ?[*:0]const u8 { return self.xwayland_surface.class; } -/// Return bounds on the dimensions of the view -pub fn getConstraints(self: Self) View.Constraints { - const hints = self.xwayland_surface.size_hints orelse return .{ - .min_width = 1, - .min_height = 1, - .max_width = math.maxInt(u31), - .max_height = math.maxInt(u31), - }; - return .{ - .min_width = @intCast(u31, math.max(hints.min_width, 1)), - .min_height = @intCast(u31, math.max(hints.min_height, 1)), - .max_width = if (hints.max_width > 0) @intCast(u31, hints.max_width) else math.maxInt(u31), - .max_height = if (hints.max_height > 0) @intCast(u31, hints.max_height) else math.maxInt(u31), - }; -} - -/// Called when the xwayland surface is destroyed fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void { const self = @fieldParentPtr(Self, "destroy", listener); |
