session-lock: use the scene graph

This commit is contained in:
Isaac Freund 2023-01-31 15:47:19 +01:00
parent 683ed0f04e
commit b38676f078
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
9 changed files with 136 additions and 63 deletions

View File

@ -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);

View File

@ -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();
@ -187,6 +205,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) {
@ -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();
};
}

View File

@ -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;

View File

@ -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 {

View File

@ -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 .{
return .{
.surface = surface,
.sx = sx,
.sy = sy,
.node = .{ .view = view },
.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 {
wlr_output.destroy();
util.gpa.destroy(node);
return;
};
ptr_node.data = &node.data;
log.debug("new output {s}", .{wlr_output.name});
self.all_outputs.append(ptr_node);
self.addOutput(&node.data);
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();
};
}
/// Remove the output from self.outputs and evacuate views if it is a member of

View File

@ -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,

View File

@ -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),

View File

@ -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 = .{

View File

@ -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});
}