aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--river/Root.zig3
-rw-r--r--river/View.zig43
-rw-r--r--river/XdgToplevel.zig23
-rw-r--r--river/XwaylandView.zig17
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);