Merge pull request #238 from pinnacle-comp/session_lock

Implement session lock
This commit is contained in:
Ottatop 2024-05-15 18:10:25 -05:00 committed by GitHub
commit 6e3b1b4960
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 501 additions and 114 deletions

6
Cargo.lock generated
View file

@ -1410,7 +1410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets 0.52.5",
"windows-targets 0.48.5",
]
[[package]]
@ -2381,7 +2381,7 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "smithay"
version = "0.3.0"
source = "git+https://github.com/Smithay/smithay?rev=95d6e79#95d6e79ed84d6e58ad75e3f1ead154dcd04719f9"
source = "git+https://github.com/Smithay/smithay?rev=0398269#0398269eab5e0e1be212d960f7010dfb53f07978"
dependencies = [
"appendlist",
"ash",
@ -2456,7 +2456,7 @@ dependencies = [
[[package]]
name = "smithay-drm-extras"
version = "0.1.0"
source = "git+https://github.com/Smithay/smithay?rev=95d6e79#95d6e79ed84d6e58ad75e3f1ead154dcd04719f9"
source = "git+https://github.com/Smithay/smithay?rev=0398269#0398269eab5e0e1be212d960f7010dfb53f07978"
dependencies = [
"drm",
"edid-rs",

View file

@ -35,7 +35,7 @@ tempfile = "3.10.1"
[workspace.dependencies.smithay]
git = "https://github.com/Smithay/smithay"
rev = "95d6e79"
rev = "0398269"
default-features = false
features = [
"desktop",
@ -77,7 +77,7 @@ keywords = ["wayland", "compositor", "smithay", "lua"]
[dependencies]
# Smithay
smithay = { workspace = true }
smithay-drm-extras = { git = "https://github.com/Smithay/smithay", rev = "95d6e79" }
smithay-drm-extras = { git = "https://github.com/Smithay/smithay", rev = "0398269" }
# Tracing
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
@ -115,7 +115,6 @@ bytemuck = "1.15.0"
pinnacle-api = { path = "./api/rust" }
gag = "1.0.0"
[build-dependencies]
vergen = { version = "8.3.1", features = ["git", "gitcl", "rustc", "cargo", "si"] }

View file

@ -33,7 +33,7 @@ use smithay::{
use tracing::error;
use crate::{
state::{Pinnacle, State, SurfaceDmabufFeedback},
state::{Pinnacle, State, SurfaceDmabufFeedback, WithState},
window::WindowElement,
};
@ -248,6 +248,16 @@ pub fn post_repaint(
if let CursorImageStatus::Surface(surf) = cursor_status {
send_frames_surface_tree(surf, output, time, Some(Duration::ZERO), |_, _| None);
}
if let Some(lock_surface) = output.with_state(|state| state.lock_surface.clone()) {
send_frames_surface_tree(
lock_surface.wl_surface(),
output,
time,
Some(Duration::ZERO),
|_, _| None,
);
}
}
impl DmabufHandler for State {

View file

@ -32,7 +32,11 @@ use smithay::{
renderer::{
self, damage,
element::{
self, surface::WaylandSurfaceRenderElement, texture::TextureBuffer, Element,
self,
solid::{SolidColorBuffer, SolidColorRenderElement},
surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
texture::TextureBuffer,
Element,
},
gles::{GlesRenderbuffer, GlesRenderer},
multigpu::{gbm::GbmGlesBackend, GpuManager, MultiRenderer, MultiTexture},
@ -88,7 +92,7 @@ use tracing::{debug, error, info, trace, warn};
use crate::{
backend::Backend,
config::ConnectorSavedState,
output::OutputName,
output::{BlankingState, OutputName},
render::{
pointer::PointerElement, pointer_render_elements, take_presentation_feedback,
OutputRenderElement,
@ -1312,6 +1316,13 @@ impl Udev {
flags,
);
}
output.with_state_mut(|state| {
if let BlankingState::Blanking = state.blanking_state {
debug!("Output {} blanked", output.name());
state.blanking_state = BlankingState::Blanked;
}
})
}
Err(err) => {
warn!("Error during rendering: {:?}", err);
@ -1399,8 +1410,6 @@ impl Udev {
texture
});
let windows = pinnacle.space.elements().cloned().collect::<Vec<_>>();
let pointer_location = pinnacle
.seat
.get_pointer()
@ -1434,10 +1443,14 @@ impl Udev {
let mut output_render_elements = Vec::new();
let should_blank = pinnacle.lock_state.is_locking()
|| (pinnacle.lock_state.is_locked()
&& output.with_state(|state| state.lock_surface.is_none()));
// If there isn't a pending screencopy that doesn't want to overlay the cursor,
// render it.
match pending_screencopy_with_cursor {
Some(include_cursor) => {
Some(include_cursor) if pinnacle.lock_state.is_unlocked() => {
if include_cursor {
// HACK: Doing `RenderFrameResult::blit_frame_result` with something on the
// | cursor plane causes the cursor to overwrite the pixels underneath it,
@ -1461,7 +1474,7 @@ impl Udev {
output_render_elements.extend(pointer_render_elements);
}
}
None => {
_ => {
let pointer_render_elements = pointer_render_elements(
output,
&mut renderer,
@ -1475,12 +1488,53 @@ impl Udev {
}
}
output_render_elements.extend(crate::render::output_render_elements(
output,
&mut renderer,
&pinnacle.space,
&windows,
));
if should_blank {
let output_size = pinnacle
.space
.output_geometry(output)
.map(|geo| geo.size)
.unwrap_or((99999, 99999).into());
let solid_color_buffer = SolidColorBuffer::new(output_size, [0.2, 0.0, 0.3, 1.0]);
let solid_color_element = SolidColorRenderElement::from_buffer(
&solid_color_buffer,
(0, 0),
output.current_scale().fractional_scale(),
1.0,
element::Kind::Unspecified,
);
output_render_elements.push(OutputRenderElement::from(solid_color_element));
output.with_state_mut(|state| {
if let BlankingState::NotBlanked = state.blanking_state {
debug!("Blanking output {} for session lock", output.name());
state.blanking_state = BlankingState::Blanking;
}
});
} else if pinnacle.lock_state.is_locked() {
if let Some(lock_surface) = output.with_state(|state| state.lock_surface.clone()) {
let elems = render_elements_from_surface_tree(
&mut renderer,
lock_surface.wl_surface(),
(0, 0),
output.current_scale().fractional_scale(),
1.0,
element::Kind::Unspecified,
);
output_render_elements.extend(elems);
}
} else {
let windows = pinnacle.space.elements().cloned().collect::<Vec<_>>();
output_render_elements.extend(crate::render::output_render_elements(
output,
&mut renderer,
&pinnacle.space,
&windows,
));
}
let result = (|| -> Result<bool, SwapBuffersError> {
let render_frame_result = render_frame(
@ -1496,13 +1550,15 @@ impl Udev {
}
}
handle_pending_screencopy(
&mut renderer,
output,
surface,
&render_frame_result,
&pinnacle.loop_handle,
);
if pinnacle.lock_state.is_unlocked() {
handle_pending_screencopy(
&mut renderer,
output,
surface,
&render_frame_result,
&pinnacle.loop_handle,
);
}
super::post_repaint(
output,

View file

@ -9,6 +9,11 @@ use smithay::{
renderer::{
self, buffer_type,
damage::{self, OutputDamageTracker, RenderOutputResult},
element::{
self,
solid::{SolidColorBuffer, SolidColorRenderElement},
surface::render_elements_from_surface_tree,
},
gles::{GlesRenderbuffer, GlesRenderer, GlesTexture},
Bind, Blit, BufferType, ExportMem, ImportDma, ImportEgl, ImportMemWl, Offscreen,
TextureFilter,
@ -37,10 +42,14 @@ use smithay::{
utils::{IsAlive, Point, Rectangle, Transform},
wayland::dmabuf::{self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
};
use tracing::{error, trace, warn};
use tracing::{debug, error, trace, warn};
use crate::{
render::{pointer::PointerElement, pointer_render_elements, take_presentation_feedback},
output::BlankingState,
render::{
pointer::PointerElement, pointer_render_elements, take_presentation_feedback,
OutputRenderElement,
},
state::{Pinnacle, State, WithState},
};
@ -200,6 +209,7 @@ impl Winit {
Some(mode),
None,
Some(Scale::Fractional(scale_factor)),
// None,
None,
);
state.pinnacle.request_layout(&output);
@ -266,19 +276,16 @@ impl State {
let mut output_render_elements = Vec::new();
let pending_screencopy_without_cursor = output.with_state(|state| {
state
.screencopy
.as_ref()
.is_some_and(|sc| !sc.overlay_cursor())
});
let should_draw_cursor = !self.pinnacle.lock_state.is_unlocked()
|| output.with_state(|state| {
// Don't draw cursor when screencopy without cursor is pending
!state
.screencopy
.as_ref()
.is_some_and(|sc| !sc.overlay_cursor())
});
// If there isn't a pending screencopy that doesn't want to overlay the cursor,
// render it.
//
// This will cause the cursor to disappear for a frame if there is one though,
// but it shouldn't meaningfully affect anything.
if !pending_screencopy_without_cursor {
if should_draw_cursor {
let pointer_location = self
.pinnacle
.seat
@ -298,12 +305,56 @@ impl State {
output_render_elements.extend(pointer_render_elements);
}
output_render_elements.extend(crate::render::output_render_elements(
output,
winit.backend.renderer(),
&self.pinnacle.space,
&windows,
));
let should_blank = self.pinnacle.lock_state.is_locking()
|| (self.pinnacle.lock_state.is_locked()
&& output.with_state(|state| state.lock_surface.is_none()));
if should_blank {
let output_size = self
.pinnacle
.space
.output_geometry(output)
.map(|geo| geo.size)
.unwrap_or((99999, 99999).into());
let solid_color_buffer = SolidColorBuffer::new(output_size, [0.2, 0.0, 0.3, 1.0]);
let solid_color_element = SolidColorRenderElement::from_buffer(
&solid_color_buffer,
(0, 0),
output.current_scale().fractional_scale(),
1.0,
element::Kind::Unspecified,
);
output_render_elements.push(OutputRenderElement::from(solid_color_element));
output.with_state_mut(|state| {
if let BlankingState::NotBlanked = state.blanking_state {
debug!("Blanking output {} for session lock", output.name());
state.blanking_state = BlankingState::Blanking;
}
});
} else if self.pinnacle.lock_state.is_locked() {
if let Some(lock_surface) = output.with_state(|state| state.lock_surface.clone()) {
let elems = render_elements_from_surface_tree(
winit.backend.renderer(),
lock_surface.wl_surface(),
(0, 0),
output.current_scale().fractional_scale(),
1.0,
element::Kind::Unspecified,
);
output_render_elements.extend(elems);
}
} else {
output_render_elements.extend(crate::render::output_render_elements(
output,
winit.backend.renderer(),
&self.pinnacle.space,
&windows,
));
}
let render_res = winit.backend.bind().and_then(|_| {
let age = if *full_redraw > 0 {
@ -325,17 +376,30 @@ impl State {
match render_res {
Ok(render_output_result) => {
Winit::handle_pending_screencopy(
&mut winit.backend,
output,
&render_output_result,
&self.pinnacle.loop_handle,
);
if self.pinnacle.lock_state.is_unlocked() {
Winit::handle_pending_screencopy(
&mut winit.backend,
output,
&render_output_result,
&self.pinnacle.loop_handle,
);
}
let has_rendered = render_output_result.damage.is_some();
if let Some(damage) = render_output_result.damage {
if let Err(err) = winit.backend.submit(Some(damage)) {
error!("Failed to submit buffer: {}", err);
match winit.backend.submit(Some(damage)) {
Ok(()) => {
output.with_state_mut(|state| {
if matches!(state.blanking_state, BlankingState::Blanking) {
// TODO: this is probably wrong
debug!("Output {} blanked", output.name());
state.blanking_state = BlankingState::Blanked;
}
});
}
Err(err) => {
error!("Failed to submit buffer: {}", err);
}
}
}

View file

@ -7,7 +7,7 @@ use smithay::{
},
reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource},
utils::{IsAlive, Serial},
wayland::seat::WaylandFocus,
wayland::{seat::WaylandFocus, session_lock::LockSurface},
};
use crate::{state::State, window::WindowElement};
@ -18,6 +18,7 @@ pub enum KeyboardFocusTarget {
Window(WindowElement),
Popup(PopupKind),
LayerSurface(LayerSurface),
LockSurface(LockSurface),
}
impl KeyboardTarget<State> for KeyboardFocusTarget {
@ -38,6 +39,9 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
KeyboardFocusTarget::LayerSurface(surf) => {
KeyboardTarget::enter(surf.wl_surface(), seat, data, keys, serial);
}
KeyboardFocusTarget::LockSurface(lock) => {
KeyboardTarget::enter(lock.wl_surface(), seat, data, keys, serial);
}
}
}
@ -52,6 +56,9 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
KeyboardFocusTarget::LayerSurface(surf) => {
KeyboardTarget::leave(surf.wl_surface(), seat, data, serial)
}
KeyboardFocusTarget::LockSurface(lock) => {
KeyboardTarget::leave(lock.wl_surface(), seat, data, serial);
}
}
}
@ -60,7 +67,7 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
seat: &Seat<State>,
data: &mut State,
key: KeysymHandle<'_>,
state: smithay::backend::input::KeyState,
state: KeyState,
serial: Serial,
time: u32,
) {
@ -74,6 +81,9 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
KeyboardFocusTarget::LayerSurface(surf) => {
KeyboardTarget::key(surf.wl_surface(), seat, data, key, state, serial, time);
}
KeyboardFocusTarget::LockSurface(lock) => {
KeyboardTarget::key(lock.wl_surface(), seat, data, key, state, serial, time);
}
}
}
@ -81,7 +91,7 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
&self,
seat: &Seat<State>,
data: &mut State,
modifiers: smithay::input::keyboard::ModifiersState,
modifiers: ModifiersState,
serial: Serial,
) {
match self {
@ -94,6 +104,9 @@ impl KeyboardTarget<State> for KeyboardFocusTarget {
KeyboardFocusTarget::LayerSurface(surf) => {
KeyboardTarget::modifiers(surf.wl_surface(), seat, data, modifiers, serial);
}
KeyboardFocusTarget::LockSurface(lock) => {
KeyboardTarget::modifiers(lock.wl_surface(), seat, data, modifiers, serial);
}
}
}
}
@ -104,6 +117,7 @@ impl IsAlive for KeyboardFocusTarget {
KeyboardFocusTarget::Window(window) => window.alive(),
KeyboardFocusTarget::Popup(popup) => popup.alive(),
KeyboardFocusTarget::LayerSurface(surf) => surf.alive(),
KeyboardFocusTarget::LockSurface(lock) => lock.alive(),
}
}
}
@ -114,6 +128,7 @@ impl WaylandFocus for KeyboardFocusTarget {
KeyboardFocusTarget::Window(window) => window.wl_surface(),
KeyboardFocusTarget::Popup(popup) => Some(popup.wl_surface().clone()),
KeyboardFocusTarget::LayerSurface(surf) => Some(surf.wl_surface().clone()),
KeyboardFocusTarget::LockSurface(lock) => Some(lock.wl_surface().clone()),
}
}
@ -127,6 +142,9 @@ impl WaylandFocus for KeyboardFocusTarget {
KeyboardFocusTarget::LayerSurface(surf) => {
surf.wl_surface().id().same_client_as(object_id)
}
KeyboardFocusTarget::LockSurface(lock) => {
lock.wl_surface().id().same_client_as(object_id)
}
}
}
}

View file

@ -414,6 +414,9 @@ impl From<KeyboardFocusTarget> for PointerFocusTarget {
KeyboardFocusTarget::LayerSurface(layer) => {
PointerFocusTarget::WlSurface(layer.wl_surface().clone())
}
KeyboardFocusTarget::LockSurface(lock) => {
PointerFocusTarget::WlSurface(lock.wl_surface().clone())
}
}
}
}

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod session_lock;
mod xdg_shell;
mod xwayland;
@ -10,7 +11,7 @@ use smithay::{
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
delegate_layer_shell, delegate_output, delegate_pointer_constraints, delegate_presentation,
delegate_primary_selection, delegate_relative_pointer, delegate_seat,
delegate_security_context, delegate_shm, delegate_viewporter,
delegate_security_context, delegate_shm, delegate_viewporter, delegate_xwayland_shell,
desktop::{
self, find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output,
utils::surface_primary_scanout_output, PopupKind, WindowSurfaceType,
@ -62,6 +63,7 @@ use smithay::{
xdg::{PopupSurface, XdgPopupSurfaceData, XdgToplevelSurfaceData},
},
shm::{ShmHandler, ShmState},
xwayland_shell::{XWaylandShellHandler, XWaylandShellState},
},
xwayland::{X11Wm, XWaylandClientData},
};
@ -129,7 +131,7 @@ impl CompositorHandler for State {
utils::on_commit_buffer_handler::<State>(surface);
X11Wm::commit_hook::<State>(surface);
X11Wm::commit_hook::<State>(self, surface);
self.backend.early_import(surface);
@ -260,6 +262,21 @@ impl CompositorHandler for State {
.cloned()
{
vec![output] // surface is a layer surface
} else if let Some(output) = self
.pinnacle
.space
.outputs()
.find(|op| {
op.with_state(|state| {
state
.lock_surface
.as_ref()
.is_some_and(|lock| lock.wl_surface() == surface)
})
})
.cloned()
{
vec![output]
} else {
return;
};
@ -830,6 +847,13 @@ impl ForeignToplevelHandler for State {
}
delegate_foreign_toplevel!(State);
impl XWaylandShellHandler for State {
fn xwayland_shell_state(&mut self) -> &mut XWaylandShellState {
&mut self.pinnacle.xwayland_shell_state
}
}
delegate_xwayland_shell!(State);
impl Pinnacle {
fn position_popup(&self, popup: &PopupSurface) {
trace!("State::position_popup");

View file

@ -0,0 +1,129 @@
use smithay::{
delegate_session_lock,
output::Output,
reexports::wayland_server::protocol::wl_output::WlOutput,
utils::SERIAL_COUNTER,
wayland::session_lock::{
LockSurface, SessionLockHandler, SessionLockManagerState, SessionLocker,
},
};
use tracing::{debug, warn};
use crate::{
focus::keyboard::KeyboardFocusTarget,
output::BlankingState,
state::{State, WithState},
};
/// State of a session lock.
#[derive(Default, Debug)]
pub enum LockState {
/// There is no session lock.
#[default]
Unlocked,
/// A session lock request came in and we are in the process of blanking outputs.
Locking(SessionLocker),
/// The session is locked.
Locked,
}
impl LockState {
/// Returns `true` if the lock state is [`Locking`].
///
/// [`Locking`]: LockState::Locking
#[must_use]
pub fn is_locking(&self) -> bool {
matches!(self, Self::Locking(..))
}
/// Returns `true` if the lock state is [`Unlocked`].
///
/// [`Unlocked`]: LockState::Unlocked
#[must_use]
pub fn is_unlocked(&self) -> bool {
matches!(self, Self::Unlocked)
}
/// Returns `true` if the lock state is [`Locked`].
///
/// [`Locked`]: LockState::Locked
#[must_use]
pub fn is_locked(&self) -> bool {
matches!(self, Self::Locked)
}
}
impl SessionLockHandler for State {
fn lock_state(&mut self) -> &mut SessionLockManagerState {
&mut self.pinnacle.session_lock_manager_state
}
fn lock(&mut self, confirmation: SessionLocker) {
debug!("Received session lock request");
self.pinnacle.lock_state = LockState::Locking(confirmation);
self.pinnacle.schedule(
|state| {
let all_outputs_blanked = state.pinnacle.space.outputs().all(|op| {
op.with_state(|st| matches!(st.blanking_state, BlankingState::Blanked))
});
!state.pinnacle.lock_state.is_locking() || all_outputs_blanked
},
|state| match std::mem::take(&mut state.pinnacle.lock_state) {
LockState::Unlocked => (),
LockState::Locking(locker) => {
debug!("Locking session");
locker.lock();
state.pinnacle.lock_state = LockState::Locked;
for output in state.pinnacle.space.outputs().cloned().collect::<Vec<_>>() {
state.schedule_render(&output);
}
}
LockState::Locked => state.pinnacle.lock_state = LockState::Locked,
},
)
}
fn unlock(&mut self) {
debug!("Session lock unlocked");
for output in self.pinnacle.space.outputs() {
output.with_state_mut(|state| {
state.lock_surface.take();
state.blanking_state = BlankingState::NotBlanked;
});
}
self.pinnacle.lock_state = LockState::Unlocked;
}
fn new_surface(&mut self, surface: LockSurface, output: WlOutput) {
let Some(output) = Output::from_resource(&output) else {
warn!(
"Session lock surface received but output doesn't exist for wl_output {output:?}"
);
return;
};
debug!("Session lock surface received for output {}", output.name());
let Some(geo) = self.pinnacle.space.output_geometry(&output) else {
return;
};
surface.with_pending_state(|state| {
state.size = Some((geo.size.w as u32, geo.size.h as u32).into())
});
surface.send_configure();
if let Some(keyboard) = self.pinnacle.seat.get_keyboard() {
keyboard.set_focus(
self,
Some(KeyboardFocusTarget::LockSurface(surface.clone())),
SERIAL_COUNTER.next_serial(),
);
}
output.with_state_mut(|state| state.lock_surface.replace(surface));
self.schedule_render(&output);
}
}
delegate_session_lock!(State);

View file

@ -531,8 +531,6 @@ impl Pinnacle {
|_| (),
)?;
let display_handle = self.display_handle.clone();
self.loop_handle
.insert_source(xwayland, move |event, _, state| match event {
XWaylandEvent::Ready {
@ -541,7 +539,6 @@ impl Pinnacle {
} => {
let mut wm = X11Wm::start_wm(
state.pinnacle.loop_handle.clone(),
display_handle.clone(),
x11_socket,
client.clone(),
)

View file

@ -155,10 +155,15 @@ impl InputState {
#[derive(Debug)]
enum KeyAction {
/// Call a config callback.
CallCallback(UnboundedSender<Result<SetKeybindResponse, tonic::Status>>),
/// Quit the compositor.
Quit,
/// Switch ttys.
SwitchVt(i32),
/// Reload the config.
ReloadConfig,
/// Prevent the key from being sent to clients.
Suppress,
}
@ -185,6 +190,17 @@ impl Pinnacle {
.output_geometry(output)
.expect("called output_geometry on unmapped output");
if !self.lock_state.is_unlocked() {
return output
.with_state(|state| state.lock_surface.clone())
.map(|lock_surface| {
(
PointerFocusTarget::WlSurface(lock_surface.wl_surface().clone()),
output_geo.loc,
)
});
}
let mut fullscreen_and_up_split_at = 0;
for (i, win) in self
@ -412,68 +428,89 @@ impl State {
device.led_update(leds);
}
for layer in self.pinnacle.layer_shell_state.layer_surfaces().rev() {
let data = compositor::with_states(layer.wl_surface(), |states| {
*states.cached_state.current::<LayerSurfaceCachedState>()
});
if data.keyboard_interactivity == KeyboardInteractivity::Exclusive
&& matches!(
data.layer,
wlr_layer::Layer::Top | wlr_layer::Layer::Overlay
)
{
let layer_surface = self.pinnacle.space.outputs().find_map(|op| {
let map = layer_map_for_output(op);
let cloned = map.layers().find(|l| l.layer_surface() == &layer).cloned();
cloned
if self.pinnacle.lock_state.is_unlocked() {
// Handle exclusive layers
for layer in self.pinnacle.layer_shell_state.layer_surfaces().rev() {
let data = compositor::with_states(layer.wl_surface(), |states| {
*states.cached_state.current::<LayerSurfaceCachedState>()
});
if data.keyboard_interactivity == KeyboardInteractivity::Exclusive
&& matches!(
data.layer,
wlr_layer::Layer::Top | wlr_layer::Layer::Overlay
)
{
let layer_surface = self.pinnacle.space.outputs().find_map(|op| {
let map = layer_map_for_output(op);
let cloned = map.layers().find(|l| l.layer_surface() == &layer).cloned();
cloned
});
if let Some(layer_surface) = layer_surface {
match self.pinnacle.input_state.exclusive_layer_focus_stack.last() {
Some(focus) => {
let layer_focus = KeyboardFocusTarget::LayerSurface(layer_surface);
if &layer_focus != focus {
if let Some(layer_surface) = layer_surface {
match self.pinnacle.input_state.exclusive_layer_focus_stack.last() {
Some(focus) => {
let layer_focus = KeyboardFocusTarget::LayerSurface(layer_surface);
if &layer_focus != focus {
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(layer_focus);
}
}
// Push the previous focus on as this is the first exclusive layer surface
// on screen. This lets us restore it when that layer surface goes away.
None => {
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(layer_focus);
.extend(keyboard.current_focus());
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(KeyboardFocusTarget::LayerSurface(layer_surface));
}
}
// Push the previous focus on as this is the first exclusive layer surface
// on screen. This lets us restore it when that layer surface goes away.
None => {
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.extend(keyboard.current_focus());
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(KeyboardFocusTarget::LayerSurface(layer_surface));
}
}
}
}
}
while let Some(last) = self.pinnacle.input_state.exclusive_layer_focus_stack.pop() {
if last.alive() {
// If it's not empty then there's another exclusive layer surface
// underneath. Otherwise `last` is the previous keyboard focus
// and we don't need the stack anymore.
if !self
.pinnacle
.input_state
.exclusive_layer_focus_stack
.is_empty()
{
self.pinnacle
while let Some(last) = self.pinnacle.input_state.exclusive_layer_focus_stack.pop() {
if last.alive() {
// If it's not empty then there's another exclusive layer surface
// underneath. Otherwise `last` is the previous keyboard focus
// and we don't need the stack anymore.
if !self
.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(last.clone());
.is_empty()
{
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(last.clone());
}
keyboard.set_focus(self, Some(last), serial);
break;
}
keyboard.set_focus(self, Some(last), serial);
break;
}
} else {
// We don't want anything but lock surfaces getting keyboard input when locked
let lock_surface = self
.pinnacle
.space
.outputs()
.find_map(|op| op.with_state(|state| state.lock_surface.clone()));
if !matches!(
keyboard.current_focus(),
Some(KeyboardFocusTarget::LockSurface(_))
) {
keyboard.set_focus(
self,
lock_surface.map(KeyboardFocusTarget::LockSurface),
serial,
);
}
}
@ -515,15 +552,23 @@ impl State {
})
})
{
return FilterResult::Intercept(KeyAction::CallCallback(sender.clone()));
if state.pinnacle.lock_state.is_unlocked() {
return FilterResult::Intercept(KeyAction::CallCallback(
sender.clone(),
));
}
}
if kill_keybind == Some((mod_mask, mod_sym)) {
return FilterResult::Intercept(KeyAction::Quit);
} else if reload_keybind == Some((mod_mask, mod_sym)) {
}
if reload_keybind == Some((mod_mask, mod_sym)) {
return FilterResult::Intercept(KeyAction::ReloadConfig);
} else if let mut vt @ keysyms::KEY_XF86Switch_VT_1
..=keysyms::KEY_XF86Switch_VT_12 = keysym.modified_sym().raw()
}
if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 =
keysym.modified_sym().raw()
{
vt = vt - keysyms::KEY_XF86Switch_VT_1 + 1;
tracing::info!("Switching to vt {vt}");

View file

@ -7,6 +7,7 @@ use smithay::{
desktop::layer_map_for_output,
output::{Mode, Output, Scale},
utils::{Logical, Point, Transform},
wayland::session_lock::LockSurface,
};
use crate::{
@ -34,6 +35,18 @@ impl OutputName {
}
}
/// State of an output's blanking status for session lock.
#[derive(Debug, Default, Copy, Clone)]
pub enum BlankingState {
/// The output is not blanked and is displaying normal content.
#[default]
NotBlanked,
/// A blank frame has been queued up.
Blanking,
/// A blank frame has been displayed.
Blanked,
}
/// The state of an output
#[derive(Default, Debug)]
pub struct OutputState {
@ -42,6 +55,8 @@ pub struct OutputState {
pub screencopy: Option<Screencopy>,
pub serial: Option<NonZeroU32>,
pub modes: Vec<Mode>,
pub lock_surface: Option<LockSurface>,
pub blanking_state: BlankingState,
}
impl WithState for Output {
@ -113,5 +128,16 @@ impl Pinnacle {
output.set_preferred(mode);
output.with_state_mut(|state| state.modes.push(mode));
}
if let Some(lock_surface) = output.with_state(|state| state.lock_surface.clone()) {
lock_surface.with_pending_state(|state| {
let Some(new_geo) = self.space.output_geometry(output) else {
return;
};
state.size = Some((new_geo.size.w as u32, new_geo.size.h as u32).into());
});
lock_surface.send_configure();
}
}
}

View file

@ -5,6 +5,7 @@ use std::{ops::Deref, sync::Mutex};
use smithay::{
backend::renderer::{
element::{
solid::SolidColorRenderElement,
surface::WaylandSurfaceRenderElement,
utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement},
AsRenderElements, RenderElementStates, Wrap,
@ -51,6 +52,7 @@ render_elements! {
Surface = WaylandSurfaceRenderElement<R>,
Pointer = PointerRenderElement<R>,
Transform = TransformRenderElement<R, E>,
Color = SolidColorRenderElement,
}
impl<R> AsRenderElements<R> for WindowElement

View file

@ -7,6 +7,7 @@ use crate::{
config::Config,
focus::OutputFocusStack,
grab::resize_grab::ResizeSurfaceState,
handlers::session_lock::LockState,
layout::LayoutState,
protocol::{
foreign_toplevel::{self, ForeignToplevelManagerState},
@ -41,10 +42,12 @@ use smithay::{
data_device::DataDeviceState, primary_selection::PrimarySelectionState,
wlr_data_control::DataControlState,
},
session_lock::SessionLockManagerState,
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
shm::ShmState,
socket::ListeningSocketSource,
viewporter::ViewporterState,
xwayland_shell::XWaylandShellState,
},
xwayland::{X11Wm, XWaylandClientData},
};
@ -94,6 +97,10 @@ pub struct Pinnacle {
pub relative_pointer_manager_state: RelativePointerManagerState,
pub pointer_constraints_state: PointerConstraintsState,
pub foreign_toplevel_manager_state: ForeignToplevelManagerState,
pub session_lock_manager_state: SessionLockManagerState,
pub xwayland_shell_state: XWaylandShellState,
pub lock_state: LockState,
/// The state of key and mousebinds along with libinput settings
pub input_state: InputState,
@ -262,6 +269,13 @@ impl Pinnacle {
&display_handle,
filter_restricted_client,
),
session_lock_manager_state: SessionLockManagerState::new::<State, _>(
&display_handle,
filter_restricted_client,
),
xwayland_shell_state: XWaylandShellState::new::<State>(&display_handle),
lock_state: LockState::default(),
input_state: InputState::new(),