session-lock: use the scene graph
This commit is contained in:
parent
683ed0f04e
commit
b38676f078
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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 = .{
|
||||
|
@ -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});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user