ext-session-lock: implement protocol
This commit is contained in:
parent
78a46c316a
commit
33187e0b09
@ -88,6 +88,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||
|
||||
const scanner = ScanProtocolsStep.create(b);
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||
scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
|
||||
scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
|
||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
||||
@ -108,6 +109,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||
scanner.generate("xdg_wm_base", 2);
|
||||
scanner.generate("zwp_pointer_gestures_v1", 3);
|
||||
scanner.generate("zwp_pointer_constraints_v1", 1);
|
||||
scanner.generate("ext_session_lock_manager_v1", 1);
|
||||
|
||||
scanner.generate("zriver_control_v1", 1);
|
||||
scanner.generate("zriver_status_manager_v1", 3);
|
||||
|
@ -154,11 +154,11 @@ are ignored by river.
|
||||
|
||||
## MAPPINGS
|
||||
|
||||
Mappings are modal in river. Each mapping is associated with a mode and is
|
||||
only active while in that mode. There are two special modes: "normal" and
|
||||
"locked". The normal mode is the initial mode on startup. The locked mode
|
||||
is automatically entered while a lock screen is active. It cannot be entered
|
||||
or exited manually.
|
||||
Mappings are modal in river. Each mapping is associated with a mode and
|
||||
is only active while in that mode. There are two special modes: "normal"
|
||||
and "locked". The normal mode is the initial mode on startup. The locked
|
||||
mode is automatically entered while the session is locked (e.g. due to
|
||||
a screenlocker). It cannot be entered or exited manually.
|
||||
|
||||
The following modifiers are available for use in mappings:
|
||||
|
||||
|
@ -32,6 +32,7 @@ const util = @import("util.zig");
|
||||
|
||||
const Config = @import("Config.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
const View = @import("View.zig");
|
||||
@ -337,7 +338,6 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
||||
fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void {
|
||||
switch (result.parent) {
|
||||
.view => |view| {
|
||||
// Otherwise focus the view
|
||||
self.seat.focus(view);
|
||||
},
|
||||
.layer_surface => |layer_surface| {
|
||||
@ -350,6 +350,10 @@ fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void {
|
||||
self.seat.focus(null);
|
||||
}
|
||||
},
|
||||
.lock_surface => |lock_surface| {
|
||||
assert(server.lock_manager.locked);
|
||||
self.seat.setFocusRaw(.{ .lock_surface = lock_surface });
|
||||
},
|
||||
.xwayland_override_redirect => |override_redirect| {
|
||||
if (!build_options.xwayland) unreachable;
|
||||
if (override_redirect.xwayland_surface.overrideRedirectWantsFocus() and
|
||||
@ -628,6 +632,7 @@ const SurfaceAtResult = struct {
|
||||
parent: union(enum) {
|
||||
view: *View,
|
||||
layer_surface: *LayerSurface,
|
||||
lock_surface: *LockSurface,
|
||||
xwayland_override_redirect: if (build_options.xwayland) *XwaylandOverrideRedirect else void,
|
||||
},
|
||||
};
|
||||
@ -650,6 +655,22 @@ fn surfaceAtCoords(lx: f64, ly: f64) ?SurfaceAtResult {
|
||||
var oy = ly;
|
||||
server.root.output_layout.outputCoords(wlr_output, &ox, &oy);
|
||||
|
||||
if (server.lock_manager.locked) {
|
||||
if (output.lock_surface) |lock_surface| {
|
||||
var sx: f64 = undefined;
|
||||
var sy: f64 = undefined;
|
||||
if (lock_surface.wlr_lock_surface.surface.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||
return SurfaceAtResult{
|
||||
.surface = found,
|
||||
.sx = sx,
|
||||
.sy = sy,
|
||||
.parent = .{ .lock_surface = lock_surface },
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the first visible fullscreen view in the stack if there is one
|
||||
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||
const fullscreen_view = while (it.next()) |view| {
|
||||
@ -1010,7 +1031,7 @@ pub fn checkFocusFollowsCursor(self: *Self) void {
|
||||
server.root.startTransaction();
|
||||
}
|
||||
},
|
||||
.layer_surface => {},
|
||||
.layer_surface, .lock_surface => {},
|
||||
.xwayland_override_redirect => assert(build_options.xwayland),
|
||||
}
|
||||
}
|
||||
@ -1050,6 +1071,7 @@ fn shouldPassthrough(self: Self) bool {
|
||||
return false;
|
||||
},
|
||||
.resize, .move => {
|
||||
assert(!server.lock_manager.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
|
||||
@ -1064,6 +1086,7 @@ fn passthrough(self: *Self, time: u32) void {
|
||||
assert(self.mode == .passthrough);
|
||||
|
||||
if (self.surfaceAt()) |result| {
|
||||
assert((result.parent == .lock_surface) == server.lock_manager.locked);
|
||||
self.seat.wlr_seat.pointerNotifyEnter(result.surface, result.sx, result.sy);
|
||||
self.seat.wlr_seat.pointerNotifyMotion(time, result.sx, result.sy);
|
||||
} else {
|
||||
|
124
river/LockManager.zig
Normal file
124
river/LockManager.zig
Normal file
@ -0,0 +1,124 @@
|
||||
// This file is part of river, a dynamic tiling wayland compositor.
|
||||
//
|
||||
// Copyright 2021 The River Developers
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const LockManager = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
|
||||
const server = &@import("main.zig").server;
|
||||
const util = @import("util.zig");
|
||||
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
|
||||
locked: bool = false,
|
||||
lock: ?*wlr.SessionLockV1 = null,
|
||||
|
||||
new_lock: wl.Listener(*wlr.SessionLockV1) = wl.Listener(*wlr.SessionLockV1).init(handleLock),
|
||||
unlock: wl.Listener(void) = wl.Listener(void).init(handleUnlock),
|
||||
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
|
||||
new_surface: wl.Listener(*wlr.SessionLockSurfaceV1) =
|
||||
wl.Listener(*wlr.SessionLockSurfaceV1).init(handleSurface),
|
||||
|
||||
pub fn init(manager: *LockManager) !void {
|
||||
manager.* = .{};
|
||||
const wlr_manager = try wlr.SessionLockManagerV1.create(server.wl_server);
|
||||
wlr_manager.events.new_lock.add(&manager.new_lock);
|
||||
}
|
||||
|
||||
pub fn deinit(manager: *LockManager) void {
|
||||
// deinit() should only be called after wl.Server.destroyClients()
|
||||
assert(manager.lock == null);
|
||||
|
||||
manager.new_lock.link.remove();
|
||||
}
|
||||
|
||||
fn handleLock(listener: *wl.Listener(*wlr.SessionLockV1), lock: *wlr.SessionLockV1) void {
|
||||
const manager = @fieldParentPtr(LockManager, "new_lock", listener);
|
||||
|
||||
if (manager.lock != null) {
|
||||
lock.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
manager.lock = lock;
|
||||
lock.sendLocked();
|
||||
|
||||
if (!manager.locked) {
|
||||
manager.locked = true;
|
||||
|
||||
var it = server.input_manager.seats.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const seat = &node.data;
|
||||
seat.setFocusRaw(.none);
|
||||
seat.cursor.updateState();
|
||||
|
||||
// Enter locked mode
|
||||
seat.prev_mode_id = seat.mode_id;
|
||||
seat.enterMode(1);
|
||||
}
|
||||
}
|
||||
|
||||
lock.events.new_surface.add(&manager.new_surface);
|
||||
lock.events.unlock.add(&manager.unlock);
|
||||
lock.events.destroy.add(&manager.destroy);
|
||||
}
|
||||
|
||||
fn handleUnlock(listener: *wl.Listener(void)) void {
|
||||
const manager = @fieldParentPtr(LockManager, "unlock", listener);
|
||||
|
||||
assert(manager.locked);
|
||||
manager.locked = false;
|
||||
|
||||
{
|
||||
var it = server.input_manager.seats.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const seat = &node.data;
|
||||
seat.setFocusRaw(.none);
|
||||
seat.focus(null);
|
||||
seat.cursor.updateState();
|
||||
|
||||
// Exit locked mode
|
||||
seat.enterMode(seat.prev_mode_id);
|
||||
}
|
||||
}
|
||||
|
||||
handleDestroy(&manager.destroy);
|
||||
}
|
||||
|
||||
fn handleDestroy(listener: *wl.Listener(void)) void {
|
||||
const manager = @fieldParentPtr(LockManager, "destroy", listener);
|
||||
|
||||
manager.new_surface.link.remove();
|
||||
manager.unlock.link.remove();
|
||||
manager.destroy.link.remove();
|
||||
|
||||
manager.lock = null;
|
||||
}
|
||||
|
||||
fn handleSurface(
|
||||
listener: *wl.Listener(*wlr.SessionLockSurfaceV1),
|
||||
wlr_lock_surface: *wlr.SessionLockSurfaceV1,
|
||||
) void {
|
||||
const manager = @fieldParentPtr(LockManager, "new_surface", listener);
|
||||
|
||||
assert(manager.locked);
|
||||
assert(manager.lock != null);
|
||||
|
||||
LockSurface.create(wlr_lock_surface);
|
||||
}
|
123
river/LockSurface.zig
Normal file
123
river/LockSurface.zig
Normal file
@ -0,0 +1,123 @@
|
||||
// This file is part of river, a dynamic tiling wayland compositor.
|
||||
//
|
||||
// Copyright 2021 The River Developers
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const LockSurface = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
|
||||
const server = &@import("main.zig").server;
|
||||
const util = @import("util.zig");
|
||||
|
||||
const Output = @import("Output.zig");
|
||||
const Subsurface = @import("Subsurface.zig");
|
||||
|
||||
wlr_lock_surface: *wlr.SessionLockSurfaceV1,
|
||||
|
||||
output_mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleOutputMode),
|
||||
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
|
||||
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
|
||||
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
|
||||
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleSubsurface),
|
||||
|
||||
pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1) void {
|
||||
const lock_surface = util.gpa.create(LockSurface) catch {
|
||||
wlr_lock_surface.resource.getClient().postNoMemory();
|
||||
return;
|
||||
};
|
||||
|
||||
lock_surface.* = .{
|
||||
.wlr_lock_surface = wlr_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.destroy);
|
||||
wlr_lock_surface.surface.events.commit.add(&lock_surface.commit);
|
||||
wlr_lock_surface.surface.events.new_subsurface.add(&lock_surface.new_subsurface);
|
||||
|
||||
handleOutputMode(&lock_surface.output_mode, wlr_lock_surface.output);
|
||||
|
||||
Subsurface.handleExisting(wlr_lock_surface.surface, .{ .lock_surface = lock_surface });
|
||||
}
|
||||
|
||||
pub fn output(lock_surface: *LockSurface) *Output {
|
||||
return @intToPtr(*Output, lock_surface.wlr_lock_surface.output.data);
|
||||
}
|
||||
|
||||
fn handleOutputMode(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
|
||||
const lock_surface = @fieldParentPtr(LockSurface, "output_mode", listener);
|
||||
|
||||
var output_width: i32 = undefined;
|
||||
var output_height: i32 = undefined;
|
||||
lock_surface.output().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);
|
||||
|
||||
{
|
||||
var it = server.input_manager.seats.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const seat = &node.data;
|
||||
if (seat.focused != .lock_surface) {
|
||||
seat.setFocusRaw(.{ .lock_surface = lock_surface });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock_surface.output().lock_surface = lock_surface;
|
||||
}
|
||||
|
||||
fn handleDestroy(listener: *wl.Listener(void)) void {
|
||||
const lock_surface = @fieldParentPtr(LockSurface, "destroy", listener);
|
||||
|
||||
lock_surface.output().lock_surface = null;
|
||||
lock_surface.output().damage.addWhole();
|
||||
|
||||
{
|
||||
var it = server.input_manager.seats.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const seat = &node.data;
|
||||
if (seat.focused == .lock_surface and seat.focused.lock_surface == lock_surface) {
|
||||
seat.setFocusRaw(.none);
|
||||
}
|
||||
seat.cursor.updateState();
|
||||
}
|
||||
}
|
||||
|
||||
lock_surface.output_mode.link.remove();
|
||||
lock_surface.map.link.remove();
|
||||
lock_surface.destroy.link.remove();
|
||||
lock_surface.commit.link.remove();
|
||||
lock_surface.new_subsurface.link.remove();
|
||||
|
||||
Subsurface.destroySubsurfaces(lock_surface.wlr_lock_surface.surface);
|
||||
|
||||
util.gpa.destroy(lock_surface);
|
||||
}
|
||||
|
||||
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
||||
const lock_surface = @fieldParentPtr(LockSurface, "commit", listener);
|
||||
|
||||
lock_surface.output().damage.addWhole();
|
||||
}
|
||||
|
||||
fn handleSubsurface(listener: *wl.Listener(*wlr.Subsurface), subsurface: *wlr.Subsurface) void {
|
||||
const lock_surface = @fieldParentPtr(LockSurface, "new_subsurface", listener);
|
||||
Subsurface.create(subsurface, .{ .lock_surface = lock_surface });
|
||||
}
|
@ -33,6 +33,7 @@ const util = @import("util.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const Layout = @import("Layout.zig");
|
||||
const LayoutDemand = @import("LayoutDemand.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const View = @import("View.zig");
|
||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||
const OutputStatus = @import("OutputStatus.zig");
|
||||
@ -67,6 +68,8 @@ usable_box: wlr.Box,
|
||||
/// The top of the stack is the "most important" view.
|
||||
views: ViewStack(View) = .{},
|
||||
|
||||
lock_surface: ?*LockSurface = null,
|
||||
|
||||
/// The double-buffered state of the output.
|
||||
current: State = State{ .tags = 1 << 0 },
|
||||
pending: State = State{ .tags = 1 << 0 },
|
||||
|
@ -35,6 +35,7 @@ const Keyboard = @import("Keyboard.zig");
|
||||
const KeyboardGroup = @import("KeyboardGroup.zig");
|
||||
const KeycodeSet = @import("KeycodeSet.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const Mapping = @import("Mapping.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const SeatStatus = @import("SeatStatus.zig");
|
||||
@ -50,6 +51,7 @@ const FocusTarget = union(enum) {
|
||||
view: *View,
|
||||
xwayland_override_redirect: *XwaylandOverrideRedirect,
|
||||
layer: *LayerSurface,
|
||||
lock_surface: *LockSurface,
|
||||
none: void,
|
||||
};
|
||||
|
||||
@ -76,7 +78,6 @@ keyboard_groups: std.TailQueue(KeyboardGroup) = .{},
|
||||
/// is currently available for focus.
|
||||
focused_output: *Output,
|
||||
|
||||
/// Currently focused view/layer surface if any
|
||||
focused: FocusTarget = .none,
|
||||
|
||||
/// Stack of views in most recently focused order
|
||||
@ -149,6 +150,9 @@ pub fn deinit(self: *Self) void {
|
||||
pub fn focus(self: *Self, _target: ?*View) void {
|
||||
var target = _target;
|
||||
|
||||
// Views may not recieve focus while locked.
|
||||
if (server.lock_manager.locked) return;
|
||||
|
||||
// While a layer surface is focused, views may not recieve focus
|
||||
if (self.focused == .layer) return;
|
||||
|
||||
@ -220,6 +224,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
break :blk target_override_redirect.xwayland_surface.surface;
|
||||
},
|
||||
.layer => |target_layer| target_layer.wlr_layer_surface.surface,
|
||||
.lock_surface => |lock_surface| lock_surface.wlr_lock_surface.surface,
|
||||
.none => null,
|
||||
};
|
||||
|
||||
@ -229,18 +234,23 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
||||
view.pending.focus -= 1;
|
||||
if (view.pending.focus == 0) view.setActivated(false);
|
||||
},
|
||||
.xwayland_override_redirect, .layer, .none => {},
|
||||
.xwayland_override_redirect, .layer, .lock_surface, .none => {},
|
||||
}
|
||||
|
||||
// Set the new focus
|
||||
switch (new_focus) {
|
||||
.view => |target_view| {
|
||||
assert(!server.lock_manager.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(self.focused_output == target_layer.output),
|
||||
.layer => |target_layer| {
|
||||
assert(!server.lock_manager.locked);
|
||||
assert(self.focused_output == target_layer.output);
|
||||
},
|
||||
.lock_surface => assert(server.lock_manager.locked),
|
||||
.xwayland_override_redirect, .none => {},
|
||||
}
|
||||
self.focused = new_focus;
|
||||
|
@ -30,6 +30,7 @@ const DecorationManager = @import("DecorationManager.zig");
|
||||
const InputManager = @import("InputManager.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const LayoutManager = @import("LayoutManager.zig");
|
||||
const LockManager = @import("LockManager.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const Root = @import("Root.zig");
|
||||
const StatusManager = @import("StatusManager.zig");
|
||||
@ -71,6 +72,7 @@ control: Control,
|
||||
status_manager: StatusManager,
|
||||
layout_manager: LayoutManager,
|
||||
idle_inhibitor_manager: IdleInhibitorManager,
|
||||
lock_manager: LockManager,
|
||||
|
||||
pub fn init(self: *Self) !void {
|
||||
self.wl_server = try wl.Server.create();
|
||||
@ -127,6 +129,7 @@ pub fn init(self: *Self) !void {
|
||||
try self.status_manager.init();
|
||||
try self.layout_manager.init();
|
||||
try self.idle_inhibitor_manager.init(self.input_manager.idle);
|
||||
try self.lock_manager.init();
|
||||
|
||||
// These all free themselves when the wl_server is destroyed
|
||||
_ = try wlr.DataDeviceManager.create(self.wl_server);
|
||||
@ -153,6 +156,7 @@ pub fn deinit(self: *Self) void {
|
||||
self.root.deinit();
|
||||
self.input_manager.deinit();
|
||||
self.idle_inhibitor_manager.deinit();
|
||||
self.lock_manager.deinit();
|
||||
|
||||
self.wl_server.destroy();
|
||||
|
||||
|
@ -26,17 +26,20 @@ const util = @import("util.zig");
|
||||
|
||||
const DragIcon = @import("DragIcon.zig");
|
||||
const LayerSurface = @import("LayerSurface.zig");
|
||||
const LockSurface = @import("LockSurface.zig");
|
||||
const XdgToplevel = @import("XdgToplevel.zig");
|
||||
|
||||
pub const Parent = union(enum) {
|
||||
xdg_toplevel: *XdgToplevel,
|
||||
layer_surface: *LayerSurface,
|
||||
lock_surface: *LockSurface,
|
||||
drag_icon: *DragIcon,
|
||||
|
||||
pub fn damageWholeOutput(parent: Parent) void {
|
||||
switch (parent) {
|
||||
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.view.output.damage.addWhole(),
|
||||
.layer_surface => |layer_surface| layer_surface.output.damage.addWhole(),
|
||||
.lock_surface => |lock_surface| lock_surface.output().damage.addWhole(),
|
||||
.drag_icon => |_| {
|
||||
var it = server.root.outputs.first;
|
||||
while (it) |node| : (it = node.next) node.data.damage.addWhole();
|
||||
|
@ -76,7 +76,7 @@ pub fn create(wlr_xdg_popup: *wlr.XdgPopup, parent: Parent) void {
|
||||
layer_surface.output.wlr_output.effectiveResolution(&box.width, &box.height);
|
||||
wlr_xdg_popup.unconstrainFromBox(&box);
|
||||
},
|
||||
.drag_icon => unreachable,
|
||||
.drag_icon, .lock_surface => unreachable,
|
||||
}
|
||||
|
||||
wlr_xdg_popup.base.events.destroy.add(&xdg_popup.surface_destroy);
|
||||
|
@ -63,6 +63,35 @@ pub fn renderOutput(output: *Output) void {
|
||||
|
||||
server.renderer.begin(@intCast(u32, output.wlr_output.width), @intCast(u32, output.wlr_output.height));
|
||||
|
||||
if (server.lock_manager.locked) {
|
||||
server.renderer.clear(&[_]f32{ 0, 0, 0, 1 }); // solid black
|
||||
|
||||
// TODO: this isn't frame-perfect if the output mode is changed. We
|
||||
// could possibly delay rendering new frames after the mode change
|
||||
// until the surface commits a buffer of the correct size.
|
||||
if (output.lock_surface) |lock_surface| {
|
||||
var rdata = SurfaceRenderData{
|
||||
.output = output,
|
||||
.output_x = 0,
|
||||
.output_y = 0,
|
||||
.when = &now,
|
||||
};
|
||||
lock_surface.wlr_lock_surface.surface.forEachSurface(
|
||||
*SurfaceRenderData,
|
||||
renderSurfaceIterator,
|
||||
&rdata,
|
||||
);
|
||||
}
|
||||
|
||||
renderDragIcons(output, &now);
|
||||
|
||||
output.wlr_output.renderSoftwareCursors(null);
|
||||
server.renderer.end();
|
||||
output.wlr_output.commit() catch
|
||||
log.err("output commit failed for {s}", .{output.wlr_output.name});
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first visible fullscreen view in the stack if there is one
|
||||
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, renderFilter);
|
||||
const fullscreen_view = while (it.next()) |view| {
|
||||
@ -131,19 +160,10 @@ pub fn renderOutput(output: *Output) void {
|
||||
|
||||
renderDragIcons(output, &now);
|
||||
|
||||
// Hardware cursors are rendered by the GPU on a separate plane, and can be
|
||||
// moved around without re-rendering what's beneath them - which is more
|
||||
// efficient. However, not all hardware supports hardware cursors. For this
|
||||
// reason, wlroots provides a software fallback, which we ask it to render
|
||||
// here. wlr_cursor handles configuring hardware vs software cursors for you,
|
||||
// and this function is a no-op when hardware cursors are in use.
|
||||
output.wlr_output.renderSoftwareCursors(null);
|
||||
|
||||
// Conclude rendering and swap the buffers, showing the final frame
|
||||
// on-screen.
|
||||
server.renderer.end();
|
||||
|
||||
// TODO: handle failure
|
||||
output.wlr_output.commit() catch
|
||||
log.err("output commit failed for {s}", .{output.wlr_output.name});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user