mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Merge pull request #238 from pinnacle-comp/session_lock
Implement session lock
This commit is contained in:
commit
6e3b1b4960
14 changed files with 501 additions and 114 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
129
src/handlers/session_lock.rs
Normal file
129
src/handlers/session_lock.rs
Normal 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);
|
|
@ -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(),
|
||||
)
|
||||
|
|
149
src/input.rs
149
src/input.rs
|
@ -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}");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
14
src/state.rs
14
src/state.rs
|
@ -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(),
|
||||
|
||||
|
|
Loading…
Reference in a new issue