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);
|
const scanner = ScanProtocolsStep.create(b);
|
||||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
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-gestures/pointer-gestures-unstable-v1.xml");
|
||||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-control-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("xdg_wm_base", 2);
|
||||||
scanner.generate("zwp_pointer_gestures_v1", 3);
|
scanner.generate("zwp_pointer_gestures_v1", 3);
|
||||||
scanner.generate("zwp_pointer_constraints_v1", 1);
|
scanner.generate("zwp_pointer_constraints_v1", 1);
|
||||||
|
scanner.generate("ext_session_lock_manager_v1", 1);
|
||||||
|
|
||||||
scanner.generate("zriver_control_v1", 1);
|
scanner.generate("zriver_control_v1", 1);
|
||||||
scanner.generate("zriver_status_manager_v1", 3);
|
scanner.generate("zriver_status_manager_v1", 3);
|
||||||
|
@ -154,11 +154,11 @@ are ignored by river.
|
|||||||
|
|
||||||
## MAPPINGS
|
## MAPPINGS
|
||||||
|
|
||||||
Mappings are modal in river. Each mapping is associated with a mode and is
|
Mappings are modal in river. Each mapping is associated with a mode and
|
||||||
only active while in that mode. There are two special modes: "normal" and
|
is only active while in that mode. There are two special modes: "normal"
|
||||||
"locked". The normal mode is the initial mode on startup. The locked mode
|
and "locked". The normal mode is the initial mode on startup. The locked
|
||||||
is automatically entered while a lock screen is active. It cannot be entered
|
mode is automatically entered while the session is locked (e.g. due to
|
||||||
or exited manually.
|
a screenlocker). It cannot be entered or exited manually.
|
||||||
|
|
||||||
The following modifiers are available for use in mappings:
|
The following modifiers are available for use in mappings:
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ const util = @import("util.zig");
|
|||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
|
const LockSurface = @import("LockSurface.zig");
|
||||||
const Output = @import("Output.zig");
|
const Output = @import("Output.zig");
|
||||||
const Seat = @import("Seat.zig");
|
const Seat = @import("Seat.zig");
|
||||||
const View = @import("View.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 {
|
fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void {
|
||||||
switch (result.parent) {
|
switch (result.parent) {
|
||||||
.view => |view| {
|
.view => |view| {
|
||||||
// Otherwise focus the view
|
|
||||||
self.seat.focus(view);
|
self.seat.focus(view);
|
||||||
},
|
},
|
||||||
.layer_surface => |layer_surface| {
|
.layer_surface => |layer_surface| {
|
||||||
@ -350,6 +350,10 @@ fn updateKeyboardFocus(self: Self, result: SurfaceAtResult) void {
|
|||||||
self.seat.focus(null);
|
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| {
|
.xwayland_override_redirect => |override_redirect| {
|
||||||
if (!build_options.xwayland) unreachable;
|
if (!build_options.xwayland) unreachable;
|
||||||
if (override_redirect.xwayland_surface.overrideRedirectWantsFocus() and
|
if (override_redirect.xwayland_surface.overrideRedirectWantsFocus() and
|
||||||
@ -628,6 +632,7 @@ const SurfaceAtResult = struct {
|
|||||||
parent: union(enum) {
|
parent: union(enum) {
|
||||||
view: *View,
|
view: *View,
|
||||||
layer_surface: *LayerSurface,
|
layer_surface: *LayerSurface,
|
||||||
|
lock_surface: *LockSurface,
|
||||||
xwayland_override_redirect: if (build_options.xwayland) *XwaylandOverrideRedirect else void,
|
xwayland_override_redirect: if (build_options.xwayland) *XwaylandOverrideRedirect else void,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -650,6 +655,22 @@ fn surfaceAtCoords(lx: f64, ly: f64) ?SurfaceAtResult {
|
|||||||
var oy = ly;
|
var oy = ly;
|
||||||
server.root.output_layout.outputCoords(wlr_output, &ox, &oy);
|
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
|
// 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);
|
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||||
const fullscreen_view = while (it.next()) |view| {
|
const fullscreen_view = while (it.next()) |view| {
|
||||||
@ -1010,7 +1031,7 @@ pub fn checkFocusFollowsCursor(self: *Self) void {
|
|||||||
server.root.startTransaction();
|
server.root.startTransaction();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.layer_surface => {},
|
.layer_surface, .lock_surface => {},
|
||||||
.xwayland_override_redirect => assert(build_options.xwayland),
|
.xwayland_override_redirect => assert(build_options.xwayland),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1050,6 +1071,7 @@ fn shouldPassthrough(self: Self) bool {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
.resize, .move => {
|
.resize, .move => {
|
||||||
|
assert(!server.lock_manager.locked);
|
||||||
const target = if (self.mode == .resize) self.mode.resize.view else self.mode.move.view;
|
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.
|
// 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
|
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);
|
assert(self.mode == .passthrough);
|
||||||
|
|
||||||
if (self.surfaceAt()) |result| {
|
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.pointerNotifyEnter(result.surface, result.sx, result.sy);
|
||||||
self.seat.wlr_seat.pointerNotifyMotion(time, result.sx, result.sy);
|
self.seat.wlr_seat.pointerNotifyMotion(time, result.sx, result.sy);
|
||||||
} else {
|
} 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 LayerSurface = @import("LayerSurface.zig");
|
||||||
const Layout = @import("Layout.zig");
|
const Layout = @import("Layout.zig");
|
||||||
const LayoutDemand = @import("LayoutDemand.zig");
|
const LayoutDemand = @import("LayoutDemand.zig");
|
||||||
|
const LockSurface = @import("LockSurface.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
const OutputStatus = @import("OutputStatus.zig");
|
const OutputStatus = @import("OutputStatus.zig");
|
||||||
@ -67,6 +68,8 @@ usable_box: wlr.Box,
|
|||||||
/// The top of the stack is the "most important" view.
|
/// The top of the stack is the "most important" view.
|
||||||
views: ViewStack(View) = .{},
|
views: ViewStack(View) = .{},
|
||||||
|
|
||||||
|
lock_surface: ?*LockSurface = null,
|
||||||
|
|
||||||
/// The double-buffered state of the output.
|
/// The double-buffered state of the output.
|
||||||
current: State = State{ .tags = 1 << 0 },
|
current: State = State{ .tags = 1 << 0 },
|
||||||
pending: 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 KeyboardGroup = @import("KeyboardGroup.zig");
|
||||||
const KeycodeSet = @import("KeycodeSet.zig");
|
const KeycodeSet = @import("KeycodeSet.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
|
const LockSurface = @import("LockSurface.zig");
|
||||||
const Mapping = @import("Mapping.zig");
|
const Mapping = @import("Mapping.zig");
|
||||||
const Output = @import("Output.zig");
|
const Output = @import("Output.zig");
|
||||||
const SeatStatus = @import("SeatStatus.zig");
|
const SeatStatus = @import("SeatStatus.zig");
|
||||||
@ -50,6 +51,7 @@ const FocusTarget = union(enum) {
|
|||||||
view: *View,
|
view: *View,
|
||||||
xwayland_override_redirect: *XwaylandOverrideRedirect,
|
xwayland_override_redirect: *XwaylandOverrideRedirect,
|
||||||
layer: *LayerSurface,
|
layer: *LayerSurface,
|
||||||
|
lock_surface: *LockSurface,
|
||||||
none: void,
|
none: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +78,6 @@ keyboard_groups: std.TailQueue(KeyboardGroup) = .{},
|
|||||||
/// is currently available for focus.
|
/// is currently available for focus.
|
||||||
focused_output: *Output,
|
focused_output: *Output,
|
||||||
|
|
||||||
/// Currently focused view/layer surface if any
|
|
||||||
focused: FocusTarget = .none,
|
focused: FocusTarget = .none,
|
||||||
|
|
||||||
/// Stack of views in most recently focused order
|
/// 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 {
|
pub fn focus(self: *Self, _target: ?*View) void {
|
||||||
var target = _target;
|
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
|
// While a layer surface is focused, views may not recieve focus
|
||||||
if (self.focused == .layer) return;
|
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;
|
break :blk target_override_redirect.xwayland_surface.surface;
|
||||||
},
|
},
|
||||||
.layer => |target_layer| target_layer.wlr_layer_surface.surface,
|
.layer => |target_layer| target_layer.wlr_layer_surface.surface,
|
||||||
|
.lock_surface => |lock_surface| lock_surface.wlr_lock_surface.surface,
|
||||||
.none => null,
|
.none => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -229,18 +234,23 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
|
|||||||
view.pending.focus -= 1;
|
view.pending.focus -= 1;
|
||||||
if (view.pending.focus == 0) view.setActivated(false);
|
if (view.pending.focus == 0) view.setActivated(false);
|
||||||
},
|
},
|
||||||
.xwayland_override_redirect, .layer, .none => {},
|
.xwayland_override_redirect, .layer, .lock_surface, .none => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the new focus
|
// Set the new focus
|
||||||
switch (new_focus) {
|
switch (new_focus) {
|
||||||
.view => |target_view| {
|
.view => |target_view| {
|
||||||
|
assert(!server.lock_manager.locked);
|
||||||
assert(self.focused_output == target_view.output);
|
assert(self.focused_output == target_view.output);
|
||||||
if (target_view.pending.focus == 0) target_view.setActivated(true);
|
if (target_view.pending.focus == 0) target_view.setActivated(true);
|
||||||
target_view.pending.focus += 1;
|
target_view.pending.focus += 1;
|
||||||
target_view.pending.urgent = false;
|
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 => {},
|
.xwayland_override_redirect, .none => {},
|
||||||
}
|
}
|
||||||
self.focused = new_focus;
|
self.focused = new_focus;
|
||||||
|
@ -30,6 +30,7 @@ const DecorationManager = @import("DecorationManager.zig");
|
|||||||
const InputManager = @import("InputManager.zig");
|
const InputManager = @import("InputManager.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
const LayoutManager = @import("LayoutManager.zig");
|
const LayoutManager = @import("LayoutManager.zig");
|
||||||
|
const LockManager = @import("LockManager.zig");
|
||||||
const Output = @import("Output.zig");
|
const Output = @import("Output.zig");
|
||||||
const Root = @import("Root.zig");
|
const Root = @import("Root.zig");
|
||||||
const StatusManager = @import("StatusManager.zig");
|
const StatusManager = @import("StatusManager.zig");
|
||||||
@ -71,6 +72,7 @@ control: Control,
|
|||||||
status_manager: StatusManager,
|
status_manager: StatusManager,
|
||||||
layout_manager: LayoutManager,
|
layout_manager: LayoutManager,
|
||||||
idle_inhibitor_manager: IdleInhibitorManager,
|
idle_inhibitor_manager: IdleInhibitorManager,
|
||||||
|
lock_manager: LockManager,
|
||||||
|
|
||||||
pub fn init(self: *Self) !void {
|
pub fn init(self: *Self) !void {
|
||||||
self.wl_server = try wl.Server.create();
|
self.wl_server = try wl.Server.create();
|
||||||
@ -127,6 +129,7 @@ pub fn init(self: *Self) !void {
|
|||||||
try self.status_manager.init();
|
try self.status_manager.init();
|
||||||
try self.layout_manager.init();
|
try self.layout_manager.init();
|
||||||
try self.idle_inhibitor_manager.init(self.input_manager.idle);
|
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
|
// These all free themselves when the wl_server is destroyed
|
||||||
_ = try wlr.DataDeviceManager.create(self.wl_server);
|
_ = try wlr.DataDeviceManager.create(self.wl_server);
|
||||||
@ -153,6 +156,7 @@ pub fn deinit(self: *Self) void {
|
|||||||
self.root.deinit();
|
self.root.deinit();
|
||||||
self.input_manager.deinit();
|
self.input_manager.deinit();
|
||||||
self.idle_inhibitor_manager.deinit();
|
self.idle_inhibitor_manager.deinit();
|
||||||
|
self.lock_manager.deinit();
|
||||||
|
|
||||||
self.wl_server.destroy();
|
self.wl_server.destroy();
|
||||||
|
|
||||||
|
@ -26,17 +26,20 @@ const util = @import("util.zig");
|
|||||||
|
|
||||||
const DragIcon = @import("DragIcon.zig");
|
const DragIcon = @import("DragIcon.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
|
const LockSurface = @import("LockSurface.zig");
|
||||||
const XdgToplevel = @import("XdgToplevel.zig");
|
const XdgToplevel = @import("XdgToplevel.zig");
|
||||||
|
|
||||||
pub const Parent = union(enum) {
|
pub const Parent = union(enum) {
|
||||||
xdg_toplevel: *XdgToplevel,
|
xdg_toplevel: *XdgToplevel,
|
||||||
layer_surface: *LayerSurface,
|
layer_surface: *LayerSurface,
|
||||||
|
lock_surface: *LockSurface,
|
||||||
drag_icon: *DragIcon,
|
drag_icon: *DragIcon,
|
||||||
|
|
||||||
pub fn damageWholeOutput(parent: Parent) void {
|
pub fn damageWholeOutput(parent: Parent) void {
|
||||||
switch (parent) {
|
switch (parent) {
|
||||||
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.view.output.damage.addWhole(),
|
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.view.output.damage.addWhole(),
|
||||||
.layer_surface => |layer_surface| layer_surface.output.damage.addWhole(),
|
.layer_surface => |layer_surface| layer_surface.output.damage.addWhole(),
|
||||||
|
.lock_surface => |lock_surface| lock_surface.output().damage.addWhole(),
|
||||||
.drag_icon => |_| {
|
.drag_icon => |_| {
|
||||||
var it = server.root.outputs.first;
|
var it = server.root.outputs.first;
|
||||||
while (it) |node| : (it = node.next) node.data.damage.addWhole();
|
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);
|
layer_surface.output.wlr_output.effectiveResolution(&box.width, &box.height);
|
||||||
wlr_xdg_popup.unconstrainFromBox(&box);
|
wlr_xdg_popup.unconstrainFromBox(&box);
|
||||||
},
|
},
|
||||||
.drag_icon => unreachable,
|
.drag_icon, .lock_surface => unreachable,
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_xdg_popup.base.events.destroy.add(&xdg_popup.surface_destroy);
|
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));
|
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
|
// 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);
|
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, renderFilter);
|
||||||
const fullscreen_view = while (it.next()) |view| {
|
const fullscreen_view = while (it.next()) |view| {
|
||||||
@ -131,19 +160,10 @@ pub fn renderOutput(output: *Output) void {
|
|||||||
|
|
||||||
renderDragIcons(output, &now);
|
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);
|
output.wlr_output.renderSoftwareCursors(null);
|
||||||
|
|
||||||
// Conclude rendering and swap the buffers, showing the final frame
|
|
||||||
// on-screen.
|
|
||||||
server.renderer.end();
|
server.renderer.end();
|
||||||
|
|
||||||
// TODO: handle failure
|
|
||||||
output.wlr_output.commit() catch
|
output.wlr_output.commit() catch
|
||||||
log.err("output commit failed for {s}", .{output.wlr_output.name});
|
log.err("output commit failed for {s}", .{output.wlr_output.name});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user