aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsaac Freund <mail@isaacfreund.com>2023-01-31 15:47:19 +0100
committerIsaac Freund <mail@isaacfreund.com>2023-02-28 14:55:58 +0100
commitb38676f0784b59a0a5b858fd947e7aad6d5872d7 (patch)
treee3df0aa07abc00bd972f824b2872da67c2e17a49
parent683ed0f04e89099e734afd5d1a8c9b76077e0f82 (diff)
downloadriver-b38676f0784b59a0a5b858fd947e7aad6d5872d7.tar.gz
river-b38676f0784b59a0a5b858fd947e7aad6d5872d7.tar.xz
session-lock: use the scene graph
-rw-r--r--river/Cursor.zig12
-rw-r--r--river/LockManager.zig35
-rw-r--r--river/LockSurface.zig26
-rw-r--r--river/Output.zig49
-rw-r--r--river/Root.zig45
-rw-r--r--river/SceneNodeData.zig2
-rw-r--r--river/Seat.zig4
-rw-r--r--river/XdgToplevel.zig2
-rw-r--r--river/render.zig22
9 files changed, 135 insertions, 62 deletions
diff --git a/river/Cursor.zig b/river/Cursor.zig
index f696a4e..953b57f 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -368,7 +368,7 @@ fn updateKeyboardFocus(self: Self, result: Root.AtResult) void {
self.seat.setFocusRaw(.{ .lock_surface = lock_surface });
},
.xwayland_override_redirect => |override_redirect| {
- assert(server.lock_manager.state == .unlocked);
+ assert(server.lock_manager.state != .locked);
override_redirect.focusIfDesired();
},
}
@@ -857,7 +857,7 @@ fn shouldPassthrough(self: Self) bool {
return false;
},
.resize, .move => {
- assert(server.lock_manager.state == .unlocked);
+ assert(server.lock_manager.state != .locked);
const target = if (self.mode == .resize) self.mode.resize.view else self.mode.move.view;
// The target view is no longer visible, is part of the layout, or is fullscreen.
return target.current.tags & target.output.current.tags == 0 or
@@ -872,8 +872,12 @@ fn passthrough(self: *Self, time: u32) void {
assert(self.mode == .passthrough);
if (server.root.at(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
- // TODO audit session lock assertions after wlr_scene upgrade
- assert((result.node == .lock_surface) == (server.lock_manager.state != .unlocked));
+ if (result.node == .lock_surface) {
+ assert(server.lock_manager.state != .unlocked);
+ } else {
+ assert(server.lock_manager.state != .locked);
+ }
+
if (result.surface) |surface| {
self.seat.wlr_seat.pointerNotifyEnter(surface, result.sx, result.sy);
self.seat.wlr_seat.pointerNotifyMotion(time, result.sx, result.sy);
diff --git a/river/LockManager.zig b/river/LockManager.zig
index eab3c4a..9757d93 100644
--- a/river/LockManager.zig
+++ b/river/LockManager.zig
@@ -130,6 +130,24 @@ fn handleLockSurfacesTimeout(manager: *LockManager) c_int {
assert(manager.state == .waiting_for_lock_surfaces);
manager.state = .waiting_for_blank;
+ {
+ var it = server.root.outputs.first;
+ while (it) |node| : (it = node.next) {
+ const output = &node.data;
+
+ switch (output.lock_render_state) {
+ .unlocked, .pending_lock_surface => {},
+ .pending_blank, .blanked, .lock_surface => {
+ assert(!output.normal_content.node.enabled);
+ assert(output.locked_content.node.enabled);
+ },
+ }
+
+ output.normal_content.node.setEnabled(false);
+ output.locked_content.node.setEnabled(true);
+ }
+ }
+
// This call is necessary in the case that all outputs in the layout are disabled.
manager.maybeLock();
@@ -188,6 +206,19 @@ fn handleUnlock(listener: *wl.Listener(void)) void {
log.info("session unlocked", .{});
{
+ var it = server.root.outputs.first;
+ while (it) |node| : (it = node.next) {
+ const output = &node.data;
+
+ assert(!output.normal_content.node.enabled);
+ output.normal_content.node.setEnabled(true);
+
+ assert(output.locked_content.node.enabled);
+ output.locked_content.node.setEnabled(false);
+ }
+ }
+
+ {
var it = server.input_manager.seats.first;
while (it) |node| : (it = node.next) {
const seat = &node.data;
@@ -230,5 +261,7 @@ fn handleSurface(
assert(manager.state != .unlocked);
assert(manager.lock != null);
- LockSurface.create(wlr_lock_surface, manager.lock.?);
+ LockSurface.create(wlr_lock_surface, manager.lock.?) catch {
+ wlr_lock_surface.resource.postNoMemory();
+ };
}
diff --git a/river/LockSurface.zig b/river/LockSurface.zig
index 541944f..0bc4ba4 100644
--- a/river/LockSurface.zig
+++ b/river/LockSurface.zig
@@ -17,6 +17,7 @@
const LockSurface = @This();
const std = @import("std");
+const assert = std.debug.assert;
const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
@@ -25,6 +26,7 @@ const util = @import("util.zig");
const Output = @import("Output.zig");
const Seat = @import("Seat.zig");
+const SceneNodeData = @import("SceneNodeData.zig");
wlr_lock_surface: *wlr.SessionLockSurfaceV1,
lock: *wlr.SessionLockV1,
@@ -33,11 +35,8 @@ output_mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleOutp
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
surface_destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
-pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLockV1) void {
- const lock_surface = util.gpa.create(LockSurface) catch {
- wlr_lock_surface.resource.getClient().postNoMemory();
- return;
- };
+pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLockV1) error{OutOfMemory}!void {
+ const lock_surface = try util.gpa.create(LockSurface);
lock_surface.* = .{
.wlr_lock_surface = wlr_lock_surface,
@@ -45,6 +44,10 @@ pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLoc
};
wlr_lock_surface.data = @ptrToInt(lock_surface);
+ const output = lock_surface.getOutput();
+ const tree = try output.locked_content.createSceneSubsurfaceTree(wlr_lock_surface.surface);
+ try SceneNodeData.attach(&tree.node, .{ .lock_surface = lock_surface });
+
wlr_lock_surface.output.events.mode.add(&lock_surface.output_mode);
wlr_lock_surface.events.map.add(&lock_surface.map);
wlr_lock_surface.events.destroy.add(&lock_surface.surface_destroy);
@@ -53,8 +56,6 @@ pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLoc
}
pub fn destroy(lock_surface: *LockSurface) void {
- lock_surface.output().lock_surface = null;
-
{
var surface_it = lock_surface.lock.surfaces.iterator(.forward);
const new_focus: Seat.FocusTarget = while (surface_it.next()) |surface| {
@@ -79,7 +80,7 @@ pub fn destroy(lock_surface: *LockSurface) void {
util.gpa.destroy(lock_surface);
}
-pub fn output(lock_surface: *LockSurface) *Output {
+fn getOutput(lock_surface: *LockSurface) *Output {
return @intToPtr(*Output, lock_surface.wlr_lock_surface.output.data);
}
@@ -88,14 +89,19 @@ fn handleOutputMode(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
var output_width: i32 = undefined;
var output_height: i32 = undefined;
- lock_surface.output().wlr_output.effectiveResolution(&output_width, &output_height);
+ lock_surface.getOutput().wlr_output.effectiveResolution(&output_width, &output_height);
_ = lock_surface.wlr_lock_surface.configure(@intCast(u32, output_width), @intCast(u32, output_height));
}
fn handleMap(listener: *wl.Listener(void)) void {
const lock_surface = @fieldParentPtr(LockSurface, "map", listener);
- lock_surface.output().lock_surface = lock_surface;
+ const output = lock_surface.getOutput();
+ assert(output.normal_content.node.enabled);
+ output.normal_content.node.setEnabled(false);
+
+ assert(!output.locked_content.node.enabled);
+ output.locked_content.node.setEnabled(true);
{
var it = server.input_manager.seats.first;
diff --git a/river/Output.zig b/river/Output.zig
index ca4ac29..83b4831 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -67,11 +67,12 @@ usable_box: wlr.Box,
/// Scene node representing the entire output.
/// Position must be updated when the output is moved in the layout.
tree: *wlr.SceneTree,
+normal_content: *wlr.SceneTree,
+locked_content: *wlr.SceneTree,
/// The top of the stack is the "most important" view.
views: ViewStack(View) = .{},
-lock_surface: ?*LockSurface = null,
/// Tracks the currently presented frame on the output as it pertains to ext-session-lock.
/// The output is initially considered blanked:
/// If using the DRM backend it will be blanked with the initial modeset.
@@ -121,18 +122,17 @@ mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode),
frame: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleFrame),
present: wl.Listener(*wlr.Output.event.Present) = wl.Listener(*wlr.Output.event.Present).init(handlePresent),
-pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
- if (!wlr_output.initRender(server.allocator, server.renderer)) return;
+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;
+
+ if (!wlr_output.initRender(server.allocator, server.renderer)) return error.InitRenderFailed;
- // Some backends don't have modes. DRM+KMS does, and we need to set a mode
- // before we can use the output. The mode is a tuple of (width, height,
- // refresh rate), and each monitor supports only a specific set of modes. We
- // just pick the monitor's preferred mode, a more sophisticated compositor
- // would let the user configure it.
if (wlr_output.preferredMode()) |preferred_mode| {
wlr_output.setMode(preferred_mode);
wlr_output.enable(true);
- wlr_output.commit() catch |err| {
+ wlr_output.commit() catch {
var it = wlr_output.modes.iterator(.forward);
while (it.next()) |mode| {
if (mode == preferred_mode) continue;
@@ -140,15 +140,18 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
wlr_output.commit() catch continue;
// This mode works, use it
break;
- } else {
- return err;
}
+ // If no mode works, then we will just leave the output disabled.
+ // Perhaps the user will want to set a custom mode using wlr-output-management.
};
}
+ const tree = try server.root.scene.tree.createSceneTree();
self.* = .{
.wlr_output = wlr_output,
- .tree = try server.root.scene.tree.createSceneTree(),
+ .tree = tree,
+ .normal_content = try tree.createSceneTree(),
+ .locked_content = try tree.createSceneTree(),
.usable_box = undefined,
};
wlr_output.data = @ptrToInt(self);
@@ -162,8 +165,8 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
// Ensure that a cursor image at the output's scale factor is loaded
// for each seat.
var it = server.input_manager.seats.first;
- while (it) |node| : (it = node.next) {
- const seat = &node.data;
+ while (it) |seat_node| : (it = seat_node.next) {
+ const seat = &seat_node.data;
seat.cursor.xcursor_manager.load(wlr_output.scale) catch
std.log.scoped(.cursor).err("failed to load xcursor theme at scale {}", .{wlr_output.scale});
}
@@ -177,6 +180,12 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
self.wlr_output.effectiveResolution(&self.usable_box.width, &self.usable_box.height);
self.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);
}
pub fn getLayer(self: *Self, layer: zwlr.LayerShellV1.Layer) *std.TailQueue(LayerSurface) {
@@ -477,8 +486,6 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
}
}
- if (self.lock_surface) |surface| surface.destroy();
-
// Remove all listeners
self.destroy.link.remove();
self.enable.link.remove();
@@ -503,11 +510,19 @@ fn handleEnable(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) vo
// already been added.
if (wlr_output.enabled) server.root.addOutput(self);
+ // We can't assert the current state of normal_content/locked_content
+ // here as this output may be newly created.
if (wlr_output.enabled) {
switch (server.lock_manager.state) {
- .unlocked => self.lock_render_state = .unlocked,
+ .unlocked => {
+ self.lock_render_state = .unlocked;
+ self.normal_content.node.setEnabled(true);
+ self.locked_content.node.setEnabled(false);
+ },
.waiting_for_lock_surfaces, .waiting_for_blank, .locked => {
assert(self.lock_render_state == .blanked);
+ self.normal_content.node.setEnabled(false);
+ self.locked_content.node.setEnabled(true);
},
}
} else {
diff --git a/river/Root.zig b/river/Root.zig
index 429bc74..2eebd68 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -103,6 +103,8 @@ pub fn init(self: *Self) !void {
.noop_output = .{
.wlr_output = noop_wlr_output,
.tree = try scene.tree.createSceneTree(),
+ .normal_content = try scene.tree.createSceneTree(),
+ .locked_content = try scene.tree.createSceneTree(),
.usable_box = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
},
};
@@ -153,14 +155,15 @@ pub fn at(self: Self, lx: f64, ly: f64) ?AtResult {
var it: ?*wlr.SceneNode = node_at;
while (it) |node| : (it = node.parent) {
if (@intToPtr(?*SceneNodeData, node.data)) |scene_node_data| {
- switch (scene_node_data.data) {
- .view => |view| return .{
- .surface = surface,
- .sx = sx,
- .sy = sy,
- .node = .{ .view = view },
+ return .{
+ .surface = surface,
+ .sx = sx,
+ .sy = sy,
+ .node = switch (scene_node_data.data) {
+ .view => |view| .{ .view = view },
+ .lock_surface => |lock_surface| .{ .lock_surface = lock_surface },
},
- }
+ };
}
}
}
@@ -168,28 +171,18 @@ pub fn at(self: Self, lx: f64, ly: f64) ?AtResult {
return null;
}
-fn handleNewOutput(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
- const self = @fieldParentPtr(Self, "new_output", listener);
- std.log.scoped(.output_manager).debug("new output {s}", .{wlr_output.name});
+fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+ const log = std.log.scoped(.output_manager);
- const node = util.gpa.create(std.TailQueue(Output).Node) catch {
- wlr_output.destroy();
- return;
- };
- node.data.init(wlr_output) catch {
- wlr_output.destroy();
- util.gpa.destroy(node);
- return;
- };
- const ptr_node = util.gpa.create(std.TailQueue(*Output).Node) catch {
+ log.debug("new output {s}", .{wlr_output.name});
+
+ Output.create(wlr_output) catch |err| {
+ switch (err) {
+ error.OutOfMemory => log.err("out of memory", .{}),
+ error.InitRenderFailed => log.err("failed to initialize renderer for output {s}", .{wlr_output.name}),
+ }
wlr_output.destroy();
- util.gpa.destroy(node);
- return;
};
- ptr_node.data = &node.data;
-
- self.all_outputs.append(ptr_node);
- self.addOutput(&node.data);
}
/// Remove the output from self.outputs and evacuate views if it is a member of
diff --git a/river/SceneNodeData.zig b/river/SceneNodeData.zig
index 36a815d..63ce09a 100644
--- a/river/SceneNodeData.zig
+++ b/river/SceneNodeData.zig
@@ -22,10 +22,12 @@ const wl = @import("wayland").server.wl;
const util = @import("util.zig");
+const LockSurface = @import("LockSurface.zig");
const View = @import("View.zig");
const Data = union(enum) {
view: *View,
+ lock_surface: *LockSurface,
};
node: *wlr.SceneNode,
diff --git a/river/Seat.zig b/river/Seat.zig
index 6f206f6..a0ac936 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -241,14 +241,14 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
// Set the new focus
switch (new_focus) {
.view => |target_view| {
- assert(server.lock_manager.state == .unlocked);
+ assert(server.lock_manager.state != .locked);
assert(self.focused_output == target_view.output);
if (target_view.pending.focus == 0) target_view.setActivated(true);
target_view.pending.focus += 1;
target_view.pending.urgent = false;
},
.layer => |target_layer| {
- assert(server.lock_manager.state == .unlocked);
+ assert(server.lock_manager.state != .locked);
assert(self.focused_output == target_layer.output);
},
.lock_surface => assert(server.lock_manager.state != .unlocked),
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index a9509ac..564189a 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -63,7 +63,7 @@ pub fn create(output: *Output, xdg_toplevel: *wlr.XdgToplevel) error{OutOfMemory
errdefer util.gpa.destroy(node);
const view = &node.view;
- const tree = try output.tree.createSceneXdgSurface(xdg_toplevel.base);
+ const tree = try output.normal_content.createSceneXdgSurface(xdg_toplevel.base);
errdefer tree.node.destroy();
try view.init(output, tree, .{ .xdg_toplevel = .{
diff --git a/river/render.zig b/river/render.zig
index b343aeb..86d14cd 100644
--- a/river/render.zig
+++ b/river/render.zig
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
+const assert = std.debug.assert;
const os = std.os;
const server = &@import("main.zig").server;
@@ -25,7 +26,26 @@ const log = std.log.scoped(.render);
pub fn renderOutput(output: *Output) void {
const scene_output = server.root.scene.getSceneOutput(output.wlr_output).?;
- if (!scene_output.commit()) {
+
+ if (scene_output.commit()) {
+ if (server.lock_manager.state == .locked or
+ (server.lock_manager.state == .waiting_for_lock_surfaces and output.locked_content.node.enabled) or
+ server.lock_manager.state == .waiting_for_blank)
+ {
+ assert(!output.normal_content.node.enabled);
+ assert(output.locked_content.node.enabled);
+
+ switch (server.lock_manager.state) {
+ .unlocked => unreachable,
+ .locked => switch (output.lock_render_state) {
+ .unlocked, .pending_blank, .pending_lock_surface => unreachable,
+ .blanked, .lock_surface => {},
+ },
+ .waiting_for_blank => output.lock_render_state = .pending_blank,
+ .waiting_for_lock_surfaces => output.lock_render_state = .pending_lock_surface,
+ }
+ }
+ } else {
log.err("output commit failed for {s}", .{output.wlr_output.name});
}