mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2026-01-09 23:05:54 +00:00
refactor: split lock manager
This commit is contained in:
parent
4c54feacbd
commit
6cbbce773f
13 changed files with 1103 additions and 873 deletions
|
|
@ -8,7 +8,7 @@ pub use rendering::femtovg::popup_window::PopupWindow;
|
||||||
|
|
||||||
pub use wayland::config::{MultiSurfaceConfig, ShellSurfaceConfig, WaylandSurfaceConfig};
|
pub use wayland::config::{MultiSurfaceConfig, ShellSurfaceConfig, WaylandSurfaceConfig};
|
||||||
pub use wayland::ops::WaylandSystemOps;
|
pub use wayland::ops::WaylandSystemOps;
|
||||||
pub use wayland::session_lock::lock_manager::{LockSurfaceOutputContext, OutputFilter};
|
pub use wayland::session_lock::{LockSurfaceOutputContext, OutputFilter};
|
||||||
pub use wayland::shell_adapter::WaylandShellSystem;
|
pub use wayland::shell_adapter::WaylandShellSystem;
|
||||||
pub use wayland::surfaces::app_state::AppState;
|
pub use wayland::surfaces::app_state::AppState;
|
||||||
pub use wayland::surfaces::popup_manager::PopupManager;
|
pub use wayland::surfaces::popup_manager::PopupManager;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::wayland::session_lock::lock_manager::LockSurfaceOutputContext;
|
use crate::wayland::session_lock::LockSurfaceOutputContext;
|
||||||
use crate::wayland::surfaces::app_state::AppState;
|
use crate::wayland::surfaces::app_state::AppState;
|
||||||
use crate::wayland::surfaces::display_metrics::DisplayMetrics;
|
use crate::wayland::surfaces::display_metrics::DisplayMetrics;
|
||||||
use crate::wayland::surfaces::surface_state::SurfaceState;
|
use crate::wayland::surfaces::surface_state::SurfaceState;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::wayland::config::ShellSurfaceConfig;
|
use crate::wayland::config::ShellSurfaceConfig;
|
||||||
use crate::wayland::session_lock::lock_manager::OutputFilter;
|
use crate::wayland::session_lock::OutputFilter;
|
||||||
use crate::wayland::surfaces::app_state::AppState;
|
use crate::wayland::surfaces::app_state::AppState;
|
||||||
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
||||||
use layer_shika_domain::value_objects::lock_state::LockState;
|
use layer_shika_domain::value_objects::lock_state::LockState;
|
||||||
|
|
|
||||||
|
|
@ -1,867 +0,0 @@
|
||||||
use crate::errors::{LayerShikaError, Result};
|
|
||||||
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
|
||||||
use crate::rendering::femtovg::renderable_window::RenderableWindow;
|
|
||||||
use crate::rendering::slint_integration::platform::CustomSlintPlatform;
|
|
||||||
use crate::wayland::session_lock::lock_context::{LockSurfaceParams, SessionLockContext};
|
|
||||||
use crate::wayland::session_lock::lock_surface::LockSurface;
|
|
||||||
use crate::wayland::surfaces::app_state::AppState;
|
|
||||||
use crate::wayland::surfaces::component_state::ComponentState;
|
|
||||||
use crate::wayland::surfaces::display_metrics::DisplayMetrics;
|
|
||||||
use crate::wayland::surfaces::keyboard_state::{KeyboardState, keysym_to_text};
|
|
||||||
use crate::wayland::surfaces::pointer_utils::wayland_button_to_slint;
|
|
||||||
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
|
||||||
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
|
||||||
use layer_shika_domain::value_objects::lock_state::LockState;
|
|
||||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
|
||||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
|
||||||
use log::info;
|
|
||||||
use slint::{
|
|
||||||
LogicalPosition, LogicalSize, SharedString, WindowPosition, WindowSize,
|
|
||||||
platform::{WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer},
|
|
||||||
};
|
|
||||||
use slint_interpreter::{CompilationResult, ComponentDefinition, ComponentInstance, Value};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use wayland_client::{
|
|
||||||
Proxy, QueueHandle, WEnum,
|
|
||||||
backend::ObjectId,
|
|
||||||
protocol::{wl_keyboard, wl_output::WlOutput, wl_pointer, wl_surface::WlSurface},
|
|
||||||
};
|
|
||||||
use wayland_protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1;
|
|
||||||
use xkbcommon::xkb;
|
|
||||||
|
|
||||||
pub type LockCallbackHandler = Rc<dyn Fn(&[Value]) -> Value>;
|
|
||||||
pub type OutputFilter = Rc<
|
|
||||||
dyn Fn(
|
|
||||||
&str,
|
|
||||||
OutputHandle,
|
|
||||||
Option<&OutputInfo>,
|
|
||||||
Option<OutputHandle>,
|
|
||||||
Option<OutputHandle>,
|
|
||||||
) -> bool,
|
|
||||||
>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct LockCallback {
|
|
||||||
name: String,
|
|
||||||
handler: LockCallbackHandler,
|
|
||||||
filter: Option<OutputFilter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LockCallback {
|
|
||||||
pub fn new(name: impl Into<String>, handler: LockCallbackHandler) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.into(),
|
|
||||||
handler,
|
|
||||||
filter: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_filter(
|
|
||||||
name: impl Into<String>,
|
|
||||||
handler: LockCallbackHandler,
|
|
||||||
filter: OutputFilter,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.into(),
|
|
||||||
handler,
|
|
||||||
filter: Some(filter),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn should_apply(
|
|
||||||
&self,
|
|
||||||
component_name: &str,
|
|
||||||
output_handle: OutputHandle,
|
|
||||||
output_info: Option<&OutputInfo>,
|
|
||||||
primary_handle: Option<OutputHandle>,
|
|
||||||
active_handle: Option<OutputHandle>,
|
|
||||||
) -> bool {
|
|
||||||
self.filter.as_ref().map_or_else(
|
|
||||||
|| true,
|
|
||||||
|f| {
|
|
||||||
f(
|
|
||||||
component_name,
|
|
||||||
output_handle,
|
|
||||||
output_info,
|
|
||||||
primary_handle,
|
|
||||||
active_handle,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_to(&self, component: &ComponentInstance) -> Result<()> {
|
|
||||||
let handler = Rc::clone(&self.handler);
|
|
||||||
component
|
|
||||||
.set_callback(&self.name, move |args| handler(args))
|
|
||||||
.map_err(|e| LayerShikaError::InvalidInput {
|
|
||||||
message: format!("Failed to register callback '{}': {e}", self.name),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ActiveLockSurface {
|
|
||||||
surface: LockSurface,
|
|
||||||
window: Rc<FemtoVGWindow>,
|
|
||||||
component: Option<ComponentState>,
|
|
||||||
scale_factor: f32,
|
|
||||||
has_fractional_scale: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LockSurfaceOutputContext {
|
|
||||||
pub output_handle: OutputHandle,
|
|
||||||
pub output_info: Option<OutputInfo>,
|
|
||||||
pub primary_handle: Option<OutputHandle>,
|
|
||||||
pub active_handle: Option<OutputHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LockConfigureContext {
|
|
||||||
scale_factor: f32,
|
|
||||||
component_definition: ComponentDefinition,
|
|
||||||
compilation_result: Option<Rc<CompilationResult>>,
|
|
||||||
platform: Rc<CustomSlintPlatform>,
|
|
||||||
callbacks: Vec<LockCallback>,
|
|
||||||
component_name: String,
|
|
||||||
output_handle: OutputHandle,
|
|
||||||
output_info: Option<OutputInfo>,
|
|
||||||
primary_handle: Option<OutputHandle>,
|
|
||||||
active_handle: Option<OutputHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveLockSurface {
|
|
||||||
fn new(surface: LockSurface, window: Rc<FemtoVGWindow>) -> Self {
|
|
||||||
Self {
|
|
||||||
has_fractional_scale: surface.fractional_scale().is_some(),
|
|
||||||
surface,
|
|
||||||
window,
|
|
||||||
component: None,
|
|
||||||
scale_factor: 1.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_configure(
|
|
||||||
&mut self,
|
|
||||||
serial: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
context: &LockConfigureContext,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.surface.handle_configure(serial, width, height);
|
|
||||||
self.scale_factor = context.scale_factor;
|
|
||||||
let dimensions = match SurfaceDimensions::calculate(width, height, context.scale_factor) {
|
|
||||||
Ok(dimensions) => dimensions,
|
|
||||||
Err(err) => {
|
|
||||||
info!("Failed to calculate lock surface dimensions: {err}");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let scaling_mode = self.scaling_mode();
|
|
||||||
info!(
|
|
||||||
"Lock surface dimensions: logical {}x{}, physical {}x{}, scale {}, mode {:?}",
|
|
||||||
dimensions.logical_width(),
|
|
||||||
dimensions.logical_height(),
|
|
||||||
dimensions.physical_width(),
|
|
||||||
dimensions.physical_height(),
|
|
||||||
context.scale_factor,
|
|
||||||
scaling_mode
|
|
||||||
);
|
|
||||||
self.configure_window(&dimensions, scaling_mode, context.scale_factor);
|
|
||||||
self.configure_surface(&dimensions, scaling_mode);
|
|
||||||
|
|
||||||
if self.component.is_none() {
|
|
||||||
context.platform.add_window(Rc::clone(&self.window));
|
|
||||||
let component = ComponentState::new(
|
|
||||||
context.component_definition.clone(),
|
|
||||||
context.compilation_result.clone(),
|
|
||||||
&self.window,
|
|
||||||
)?;
|
|
||||||
self.window
|
|
||||||
.window()
|
|
||||||
.dispatch_event(WindowEvent::WindowActiveChanged(true));
|
|
||||||
for callback in &context.callbacks {
|
|
||||||
if callback.should_apply(
|
|
||||||
&context.component_name,
|
|
||||||
context.output_handle,
|
|
||||||
context.output_info.as_ref(),
|
|
||||||
context.primary_handle,
|
|
||||||
context.active_handle,
|
|
||||||
) {
|
|
||||||
if let Err(err) = callback.apply_to(component.component_instance()) {
|
|
||||||
info!(
|
|
||||||
"Failed to register lock callback '{}': {err}",
|
|
||||||
callback.name
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
info!("Registered lock callback '{}'", callback.name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info!(
|
|
||||||
"Skipping callback '{}' due to selector filter (output {:?})",
|
|
||||||
callback.name, context.output_handle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.component = Some(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderableWindow::request_redraw(self.window.as_ref());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_frame_if_dirty(&self) -> Result<()> {
|
|
||||||
self.window.render_frame_if_dirty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_fractional_scale(&mut self, scale_120ths: u32) {
|
|
||||||
let scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths);
|
|
||||||
self.scale_factor = scale_factor;
|
|
||||||
if self.surface.width() == 0 || self.surface.height() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Ok(dimensions) =
|
|
||||||
SurfaceDimensions::calculate(self.surface.width(), self.surface.height(), scale_factor)
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let scaling_mode = self.scaling_mode();
|
|
||||||
self.configure_window(&dimensions, scaling_mode, scale_factor);
|
|
||||||
self.configure_surface(&dimensions, scaling_mode);
|
|
||||||
RenderableWindow::request_redraw(self.window.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_callback(&self, callback: &LockCallback) {
|
|
||||||
if let Some(component) = self.component.as_ref() {
|
|
||||||
if let Err(err) = callback.apply_to(component.component_instance()) {
|
|
||||||
info!(
|
|
||||||
"Failed to register lock callback '{}': {err}",
|
|
||||||
callback.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scaling_mode(&self) -> LockScalingMode {
|
|
||||||
if self.surface.has_fractional_scale() && self.surface.has_viewport() {
|
|
||||||
LockScalingMode::FractionalWithViewport
|
|
||||||
} else if self.surface.has_fractional_scale() {
|
|
||||||
LockScalingMode::FractionalOnly
|
|
||||||
} else {
|
|
||||||
LockScalingMode::Integer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
fn configure_window(
|
|
||||||
&self,
|
|
||||||
dimensions: &SurfaceDimensions,
|
|
||||||
mode: LockScalingMode,
|
|
||||||
scale_factor: f32,
|
|
||||||
) {
|
|
||||||
match mode {
|
|
||||||
LockScalingMode::FractionalWithViewport | LockScalingMode::FractionalOnly => {
|
|
||||||
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
|
|
||||||
self.window.set_size(WindowSize::Logical(LogicalSize::new(
|
|
||||||
dimensions.logical_width() as f32,
|
|
||||||
dimensions.logical_height() as f32,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
LockScalingMode::Integer => {
|
|
||||||
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
|
|
||||||
self.window
|
|
||||||
.set_size(WindowSize::Physical(slint::PhysicalSize::new(
|
|
||||||
dimensions.physical_width(),
|
|
||||||
dimensions.physical_height(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_surface(&self, dimensions: &SurfaceDimensions, mode: LockScalingMode) {
|
|
||||||
match mode {
|
|
||||||
LockScalingMode::FractionalWithViewport => {
|
|
||||||
self.surface.configure_fractional_viewport(
|
|
||||||
dimensions.logical_width(),
|
|
||||||
dimensions.logical_height(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
LockScalingMode::FractionalOnly | LockScalingMode::Integer => {
|
|
||||||
self.surface
|
|
||||||
.configure_buffer_scale(dimensions.buffer_scale());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
|
||||||
fn to_logical_position(&self, surface_x: f64, surface_y: f64) -> LogicalPosition {
|
|
||||||
if self.has_fractional_scale {
|
|
||||||
let x = surface_x as f32;
|
|
||||||
let y = surface_y as f32;
|
|
||||||
LogicalPosition::new(x, y)
|
|
||||||
} else {
|
|
||||||
let x = (surface_x / f64::from(self.scale_factor)) as f32;
|
|
||||||
let y = (surface_y / f64::from(self.scale_factor)) as f32;
|
|
||||||
LogicalPosition::new(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_event(&self, event: WindowEvent) {
|
|
||||||
self.window.window().dispatch_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_rc(&self) -> Rc<FemtoVGWindow> {
|
|
||||||
Rc::clone(&self.window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
enum LockScalingMode {
|
|
||||||
FractionalWithViewport,
|
|
||||||
FractionalOnly,
|
|
||||||
Integer,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SessionLockManager {
|
|
||||||
context: Rc<SessionLockContext>,
|
|
||||||
session_lock: Option<ExtSessionLockV1>,
|
|
||||||
lock_surfaces: HashMap<ObjectId, ActiveLockSurface>,
|
|
||||||
state: LockState,
|
|
||||||
config: LockConfig,
|
|
||||||
component_definition: ComponentDefinition,
|
|
||||||
compilation_result: Option<Rc<CompilationResult>>,
|
|
||||||
platform: Rc<CustomSlintPlatform>,
|
|
||||||
callbacks: Vec<LockCallback>,
|
|
||||||
active_pointer_surface_id: Option<ObjectId>,
|
|
||||||
keyboard_focus_surface_id: Option<ObjectId>,
|
|
||||||
current_pointer_position: LogicalPosition,
|
|
||||||
accumulated_axis_x: f32,
|
|
||||||
accumulated_axis_y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SessionLockManager {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(
|
|
||||||
context: Rc<SessionLockContext>,
|
|
||||||
component_definition: ComponentDefinition,
|
|
||||||
compilation_result: Option<Rc<CompilationResult>>,
|
|
||||||
platform: Rc<CustomSlintPlatform>,
|
|
||||||
config: LockConfig,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
context,
|
|
||||||
session_lock: None,
|
|
||||||
lock_surfaces: HashMap::new(),
|
|
||||||
state: LockState::Inactive,
|
|
||||||
config,
|
|
||||||
component_definition,
|
|
||||||
compilation_result,
|
|
||||||
platform,
|
|
||||||
callbacks: Vec::new(),
|
|
||||||
active_pointer_surface_id: None,
|
|
||||||
keyboard_focus_surface_id: None,
|
|
||||||
current_pointer_position: LogicalPosition::new(0.0, 0.0),
|
|
||||||
accumulated_axis_x: 0.0,
|
|
||||||
accumulated_axis_y: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn state(&self) -> LockState {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn activate(
|
|
||||||
&mut self,
|
|
||||||
outputs: impl IntoIterator<Item = WlOutput>,
|
|
||||||
queue_handle: &QueueHandle<AppState>,
|
|
||||||
) -> Result<()> {
|
|
||||||
if !self.state.can_activate() {
|
|
||||||
return Err(LayerShikaError::InvalidInput {
|
|
||||||
message: format!("Session lock cannot activate in state {:?}", self.state),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.config.validate()?;
|
|
||||||
|
|
||||||
let session_lock = self.context.lock_manager().lock(queue_handle, ());
|
|
||||||
self.session_lock = Some(session_lock.clone());
|
|
||||||
self.state = LockState::Locking;
|
|
||||||
|
|
||||||
for output in outputs {
|
|
||||||
let params = LockSurfaceParams {
|
|
||||||
compositor: self.context.compositor(),
|
|
||||||
output: &output,
|
|
||||||
session_lock: &session_lock,
|
|
||||||
fractional_scale_manager: self.context.fractional_scale_manager(),
|
|
||||||
viewporter: self.context.viewporter(),
|
|
||||||
queue_handle,
|
|
||||||
};
|
|
||||||
let surface = LockSurface::create(¶ms);
|
|
||||||
let surface_id = surface.surface_id();
|
|
||||||
let window = self.create_window(&surface_id)?;
|
|
||||||
self.lock_surfaces
|
|
||||||
.insert(output.id(), ActiveLockSurface::new(surface, window));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_locked(&mut self) {
|
|
||||||
if self.state == LockState::Locking {
|
|
||||||
info!("Session lock transitioned to Locked");
|
|
||||||
self.state = LockState::Locked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deactivate(&mut self) -> Result<()> {
|
|
||||||
if !self.state.can_deactivate() {
|
|
||||||
return Err(LayerShikaError::InvalidInput {
|
|
||||||
message: format!("Session lock cannot deactivate in state {:?}", self.state),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(session_lock) = self.session_lock.take() else {
|
|
||||||
return Err(LayerShikaError::InvalidInput {
|
|
||||||
message: "Session lock object missing during deactivate".to_string(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
for surface in self.lock_surfaces.values() {
|
|
||||||
surface.surface.destroy();
|
|
||||||
}
|
|
||||||
session_lock.unlock_and_destroy();
|
|
||||||
self.lock_surfaces.clear();
|
|
||||||
self.active_pointer_surface_id = None;
|
|
||||||
self.keyboard_focus_surface_id = None;
|
|
||||||
self.state = LockState::Unlocking;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_finished(&mut self) {
|
|
||||||
info!("Session lock finished");
|
|
||||||
self.lock_surfaces.clear();
|
|
||||||
self.session_lock = None;
|
|
||||||
self.state = LockState::Inactive;
|
|
||||||
self.active_pointer_surface_id = None;
|
|
||||||
self.keyboard_focus_surface_id = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_output(
|
|
||||||
&mut self,
|
|
||||||
output: &WlOutput,
|
|
||||||
queue_handle: &QueueHandle<AppState>,
|
|
||||||
) -> Result<()> {
|
|
||||||
if self.state != LockState::Locked {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_id = output.id();
|
|
||||||
if self.lock_surfaces.contains_key(&output_id) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(session_lock) = self.session_lock.as_ref() else {
|
|
||||||
return Err(LayerShikaError::InvalidInput {
|
|
||||||
message: "Session lock object missing during output hotplug".to_string(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("Adding lock surface for output {output_id:?}");
|
|
||||||
let params = LockSurfaceParams {
|
|
||||||
compositor: self.context.compositor(),
|
|
||||||
output,
|
|
||||||
session_lock,
|
|
||||||
fractional_scale_manager: self.context.fractional_scale_manager(),
|
|
||||||
viewporter: self.context.viewporter(),
|
|
||||||
queue_handle,
|
|
||||||
};
|
|
||||||
let surface = LockSurface::create(¶ms);
|
|
||||||
let surface_id = surface.surface_id();
|
|
||||||
let window = self.create_window(&surface_id)?;
|
|
||||||
self.lock_surfaces
|
|
||||||
.insert(output_id, ActiveLockSurface::new(surface, window));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_output(&mut self, output_id: &ObjectId) {
|
|
||||||
if let Some(surface) = self.lock_surfaces.remove(output_id) {
|
|
||||||
let surface_id = surface.surface.surface_id();
|
|
||||||
if self.active_pointer_surface_id.as_ref() == Some(&surface_id) {
|
|
||||||
self.active_pointer_surface_id = None;
|
|
||||||
}
|
|
||||||
if self.keyboard_focus_surface_id.as_ref() == Some(&surface_id) {
|
|
||||||
self.keyboard_focus_surface_id = None;
|
|
||||||
}
|
|
||||||
drop(surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_surface_by_lock_surface_id_mut(
|
|
||||||
&mut self,
|
|
||||||
lock_surface_id: &ObjectId,
|
|
||||||
) -> Option<&mut ActiveLockSurface> {
|
|
||||||
self.lock_surfaces
|
|
||||||
.values_mut()
|
|
||||||
.find(|surface| surface.surface.lock_surface_id() == *lock_surface_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_surface_by_surface_id(&self, surface_id: &ObjectId) -> Option<&ActiveLockSurface> {
|
|
||||||
self.lock_surfaces
|
|
||||||
.values()
|
|
||||||
.find(|surface| surface.surface.surface_id() == *surface_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_output_id_for_lock_surface(&self, lock_surface_id: &ObjectId) -> Option<ObjectId> {
|
|
||||||
self.lock_surfaces
|
|
||||||
.iter()
|
|
||||||
.find(|(_, surface)| surface.surface.surface_id() == *lock_surface_id)
|
|
||||||
.map(|(id, _)| id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_configure(
|
|
||||||
&mut self,
|
|
||||||
lock_surface_id: &ObjectId,
|
|
||||||
serial: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
output_ctx: LockSurfaceOutputContext,
|
|
||||||
) -> Result<()> {
|
|
||||||
let component_name = self.component_definition.name().to_string();
|
|
||||||
|
|
||||||
let context = LockConfigureContext {
|
|
||||||
scale_factor: self.config.scale_factor.value(),
|
|
||||||
component_definition: self.component_definition.clone(),
|
|
||||||
compilation_result: self.compilation_result.clone(),
|
|
||||||
platform: Rc::clone(&self.platform),
|
|
||||||
callbacks: self.callbacks.clone(),
|
|
||||||
component_name,
|
|
||||||
output_handle: output_ctx.output_handle,
|
|
||||||
output_info: output_ctx.output_info,
|
|
||||||
primary_handle: output_ctx.primary_handle,
|
|
||||||
active_handle: output_ctx.active_handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(surface) = self.find_surface_by_lock_surface_id_mut(lock_surface_id) else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
surface.handle_configure(serial, width, height, &context)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_frames(&self) -> Result<()> {
|
|
||||||
for surface in self.lock_surfaces.values() {
|
|
||||||
surface.render_frame_if_dirty()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn register_callback(&mut self, callback: LockCallback) {
|
|
||||||
for surface in self.lock_surfaces.values() {
|
|
||||||
surface.apply_callback(&callback);
|
|
||||||
}
|
|
||||||
self.callbacks.push(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_fractional_scale(&mut self, fractional_scale_id: &ObjectId, scale_120ths: u32) {
|
|
||||||
for surface in self.lock_surfaces.values_mut() {
|
|
||||||
let matches = surface
|
|
||||||
.surface
|
|
||||||
.fractional_scale()
|
|
||||||
.is_some_and(|fs| fs.id() == *fractional_scale_id);
|
|
||||||
if matches {
|
|
||||||
surface.handle_fractional_scale(scale_120ths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_lock_surface(&self, surface_id: &ObjectId) -> bool {
|
|
||||||
self.find_surface_by_surface_id(surface_id).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn has_active_pointer(&self) -> bool {
|
|
||||||
self.active_pointer_surface_id.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn has_keyboard_focus(&self) -> bool {
|
|
||||||
self.keyboard_focus_surface_id.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_pointer_enter(
|
|
||||||
&mut self,
|
|
||||||
_serial: u32,
|
|
||||||
surface: &WlSurface,
|
|
||||||
surface_x: f64,
|
|
||||||
surface_y: f64,
|
|
||||||
) -> bool {
|
|
||||||
let surface_id = surface.id();
|
|
||||||
let (position, window) = {
|
|
||||||
let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
(
|
|
||||||
active_surface.to_logical_position(surface_x, surface_y),
|
|
||||||
active_surface.window_rc(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.active_pointer_surface_id = Some(surface_id.clone());
|
|
||||||
self.current_pointer_position = position;
|
|
||||||
info!("Lock pointer enter on {:?}", surface_id);
|
|
||||||
window
|
|
||||||
.window()
|
|
||||||
.dispatch_event(WindowEvent::PointerMoved { position });
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_pointer_motion(&mut self, surface_x: f64, surface_y: f64) -> bool {
|
|
||||||
let Some(surface_id) = self.active_pointer_surface_id.clone() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let (position, window) = {
|
|
||||||
let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
(
|
|
||||||
active_surface.to_logical_position(surface_x, surface_y),
|
|
||||||
active_surface.window_rc(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.current_pointer_position = position;
|
|
||||||
window
|
|
||||||
.window()
|
|
||||||
.dispatch_event(WindowEvent::PointerMoved { position });
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_pointer_leave(&mut self) -> bool {
|
|
||||||
let Some(surface_id) = self.active_pointer_surface_id.take() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) {
|
|
||||||
active_surface.dispatch_event(WindowEvent::PointerExited);
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_pointer_button(
|
|
||||||
&mut self,
|
|
||||||
_serial: u32,
|
|
||||||
button: u32,
|
|
||||||
button_state: WEnum<wl_pointer::ButtonState>,
|
|
||||||
) -> bool {
|
|
||||||
let Some(surface_id) = self.active_pointer_surface_id.clone() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let window = {
|
|
||||||
let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
active_surface.window_rc()
|
|
||||||
};
|
|
||||||
|
|
||||||
let position = self.current_pointer_position;
|
|
||||||
let slint_button = wayland_button_to_slint(button);
|
|
||||||
let event = match button_state {
|
|
||||||
WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed {
|
|
||||||
button: slint_button,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
WEnum::Value(wl_pointer::ButtonState::Released) => WindowEvent::PointerReleased {
|
|
||||||
button: slint_button,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
_ => return true,
|
|
||||||
};
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Lock pointer button {:?} at {:?} (scale {})",
|
|
||||||
button_state,
|
|
||||||
position,
|
|
||||||
self.config.scale_factor.value()
|
|
||||||
);
|
|
||||||
window.window().dispatch_event(event);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_axis_source(&mut self, _axis_source: wl_pointer::AxisSource) -> bool {
|
|
||||||
if self.active_pointer_surface_id.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_axis(&mut self, axis: wl_pointer::Axis, value: f64) -> bool {
|
|
||||||
if self.active_pointer_surface_id.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match axis {
|
|
||||||
wl_pointer::Axis::HorizontalScroll => {
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
|
||||||
let delta = value as f32;
|
|
||||||
self.accumulated_axis_x += delta;
|
|
||||||
}
|
|
||||||
wl_pointer::Axis::VerticalScroll => {
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
|
||||||
let delta = value as f32;
|
|
||||||
self.accumulated_axis_y += delta;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_axis_discrete(&mut self, axis: wl_pointer::Axis, discrete: i32) -> bool {
|
|
||||||
if self.active_pointer_surface_id.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
let delta = (discrete as f32) * 60.0;
|
|
||||||
match axis {
|
|
||||||
wl_pointer::Axis::HorizontalScroll => {
|
|
||||||
self.accumulated_axis_x += delta;
|
|
||||||
}
|
|
||||||
wl_pointer::Axis::VerticalScroll => {
|
|
||||||
self.accumulated_axis_y += delta;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_axis_stop(&mut self, _axis: wl_pointer::Axis) -> bool {
|
|
||||||
self.active_pointer_surface_id.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_pointer_frame(&mut self) -> bool {
|
|
||||||
let Some(surface_id) = self.active_pointer_surface_id.clone() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let delta_x = self.accumulated_axis_x;
|
|
||||||
let delta_y = self.accumulated_axis_y;
|
|
||||||
self.accumulated_axis_x = 0.0;
|
|
||||||
self.accumulated_axis_y = 0.0;
|
|
||||||
|
|
||||||
let window = {
|
|
||||||
let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
active_surface.window_rc()
|
|
||||||
};
|
|
||||||
|
|
||||||
if delta_x.abs() > f32::EPSILON || delta_y.abs() > f32::EPSILON {
|
|
||||||
let position = self.current_pointer_position;
|
|
||||||
window
|
|
||||||
.window()
|
|
||||||
.dispatch_event(WindowEvent::PointerScrolled {
|
|
||||||
position,
|
|
||||||
delta_x,
|
|
||||||
delta_y,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_keyboard_enter(&mut self, surface: &WlSurface) -> bool {
|
|
||||||
let surface_id = surface.id();
|
|
||||||
if self.find_surface_by_surface_id(&surface_id).is_some() {
|
|
||||||
self.keyboard_focus_surface_id = Some(surface_id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_keyboard_leave(&mut self, surface: &WlSurface) -> bool {
|
|
||||||
let surface_id = surface.id();
|
|
||||||
if self.keyboard_focus_surface_id.as_ref() == Some(&surface_id) {
|
|
||||||
self.keyboard_focus_surface_id = None;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_keyboard_key(
|
|
||||||
&mut self,
|
|
||||||
key: u32,
|
|
||||||
state: wl_keyboard::KeyState,
|
|
||||||
keyboard_state: &mut KeyboardState,
|
|
||||||
) -> bool {
|
|
||||||
let Some(surface_id) = self.keyboard_focus_surface_id.clone() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let Some(active_surface) = self.find_surface_by_surface_id(&surface_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let Some(xkb_state) = keyboard_state.xkb_state.as_mut() else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
let keycode = xkb::Keycode::new(key + 8);
|
|
||||||
let direction = match state {
|
|
||||||
wl_keyboard::KeyState::Pressed => xkb::KeyDirection::Down,
|
|
||||||
wl_keyboard::KeyState::Released => xkb::KeyDirection::Up,
|
|
||||||
_ => return true,
|
|
||||||
};
|
|
||||||
|
|
||||||
xkb_state.update_key(keycode, direction);
|
|
||||||
|
|
||||||
let text = xkb_state.key_get_utf8(keycode);
|
|
||||||
let text = if text.is_empty() {
|
|
||||||
let keysym = xkb_state.key_get_one_sym(keycode);
|
|
||||||
keysym_to_text(keysym)
|
|
||||||
} else {
|
|
||||||
Some(SharedString::from(text.as_str()))
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(text) = text else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
let event = match state {
|
|
||||||
wl_keyboard::KeyState::Pressed => WindowEvent::KeyPressed { text },
|
|
||||||
wl_keyboard::KeyState::Released => WindowEvent::KeyReleased { text },
|
|
||||||
_ => return true,
|
|
||||||
};
|
|
||||||
info!("Lock key event {:?}", state);
|
|
||||||
active_surface.dispatch_event(event);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_window(&self, surface_id: &ObjectId) -> Result<Rc<FemtoVGWindow>> {
|
|
||||||
let init_size = LogicalSize::new(1.0, 1.0);
|
|
||||||
let context = self.context.render_factory().create_context(
|
|
||||||
surface_id,
|
|
||||||
init_size.to_physical(self.config.scale_factor.value()),
|
|
||||||
)?;
|
|
||||||
let renderer = FemtoVGRenderer::new(context)
|
|
||||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
|
||||||
let window = FemtoVGWindow::new(renderer);
|
|
||||||
RenderableWindow::set_scale_factor(window.as_ref(), self.config.scale_factor.value());
|
|
||||||
window.set_size(WindowSize::Logical(init_size));
|
|
||||||
window.set_position(WindowPosition::Logical(LogicalPosition::new(0., 0.)));
|
|
||||||
Ok(window)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_lock_surfaces(&self, f: &mut dyn FnMut(&ObjectId, &ComponentInstance)) {
|
|
||||||
for (output_id, active_surface) in &self.lock_surfaces {
|
|
||||||
if let Some(component) = active_surface.component.as_ref() {
|
|
||||||
f(output_id, component.component_instance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn component_name(&self) -> &ComponentDefinition {
|
|
||||||
&self.component_definition
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count_lock_surfaces(&self) -> usize {
|
|
||||||
self.lock_surfaces
|
|
||||||
.values()
|
|
||||||
.filter(|s| s.component.is_some())
|
|
||||||
.count()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::errors::{LayerShikaError, Result};
|
||||||
|
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||||
|
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||||
|
use slint_interpreter::{ComponentInstance, Value};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub type LockCallbackHandler = Rc<dyn Fn(&[Value]) -> Value>;
|
||||||
|
pub type OutputFilter = Rc<
|
||||||
|
dyn Fn(
|
||||||
|
&str,
|
||||||
|
OutputHandle,
|
||||||
|
Option<&OutputInfo>,
|
||||||
|
Option<OutputHandle>,
|
||||||
|
Option<OutputHandle>,
|
||||||
|
) -> bool,
|
||||||
|
>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LockCallback {
|
||||||
|
name: String,
|
||||||
|
handler: LockCallbackHandler,
|
||||||
|
filter: Option<OutputFilter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockCallback {
|
||||||
|
pub fn new(name: impl Into<String>, handler: LockCallbackHandler) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
handler,
|
||||||
|
filter: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_filter(
|
||||||
|
name: impl Into<String>,
|
||||||
|
handler: LockCallbackHandler,
|
||||||
|
filter: OutputFilter,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
handler,
|
||||||
|
filter: Some(filter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_apply(
|
||||||
|
&self,
|
||||||
|
component_name: &str,
|
||||||
|
output_handle: OutputHandle,
|
||||||
|
output_info: Option<&OutputInfo>,
|
||||||
|
primary_handle: Option<OutputHandle>,
|
||||||
|
active_handle: Option<OutputHandle>,
|
||||||
|
) -> bool {
|
||||||
|
self.filter.as_ref().map_or_else(
|
||||||
|
|| true,
|
||||||
|
|f| {
|
||||||
|
f(
|
||||||
|
component_name,
|
||||||
|
output_handle,
|
||||||
|
output_info,
|
||||||
|
primary_handle,
|
||||||
|
active_handle,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_to(&self, component: &ComponentInstance) -> Result<()> {
|
||||||
|
let handler = Rc::clone(&self.handler);
|
||||||
|
component
|
||||||
|
.set_callback(&self.name, move |args| handler(args))
|
||||||
|
.map_err(|e| LayerShikaError::InvalidInput {
|
||||||
|
message: format!("Failed to register callback '{}': {e}", self.name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn name(&self) -> &String {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,331 @@
|
||||||
|
use crate::wayland::surfaces::keyboard_state::{KeyboardState, keysym_to_text};
|
||||||
|
use crate::wayland::surfaces::pointer_utils::wayland_button_to_slint;
|
||||||
|
use log::info;
|
||||||
|
use slint::{
|
||||||
|
LogicalPosition, SharedString,
|
||||||
|
platform::{WindowAdapter, WindowEvent},
|
||||||
|
};
|
||||||
|
use wayland_client::{
|
||||||
|
Proxy, WEnum,
|
||||||
|
backend::ObjectId,
|
||||||
|
protocol::{wl_keyboard, wl_pointer, wl_surface::WlSurface},
|
||||||
|
};
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
use super::state::ActiveLockSurface;
|
||||||
|
|
||||||
|
pub(super) struct InputState {
|
||||||
|
pub active_pointer_surface_id: Option<ObjectId>,
|
||||||
|
pub keyboard_focus_surface_id: Option<ObjectId>,
|
||||||
|
pub current_pointer_position: LogicalPosition,
|
||||||
|
pub accumulated_axis_x: f32,
|
||||||
|
pub accumulated_axis_y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
active_pointer_surface_id: None,
|
||||||
|
keyboard_focus_surface_id: None,
|
||||||
|
current_pointer_position: LogicalPosition::new(0.0, 0.0),
|
||||||
|
accumulated_axis_x: 0.0,
|
||||||
|
accumulated_axis_y: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.active_pointer_surface_id = None;
|
||||||
|
self.keyboard_focus_surface_id = None;
|
||||||
|
self.current_pointer_position = LogicalPosition::new(0.0, 0.0);
|
||||||
|
self.accumulated_axis_x = 0.0;
|
||||||
|
self.accumulated_axis_y = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_surface_refs(&mut self, surface_id: &ObjectId) {
|
||||||
|
if self.active_pointer_surface_id.as_ref() == Some(surface_id) {
|
||||||
|
self.active_pointer_surface_id = None;
|
||||||
|
}
|
||||||
|
if self.keyboard_focus_surface_id.as_ref() == Some(surface_id) {
|
||||||
|
self.keyboard_focus_surface_id = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn has_active_pointer(&self) -> bool {
|
||||||
|
self.active_pointer_surface_id.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn has_keyboard_focus(&self) -> bool {
|
||||||
|
self.keyboard_focus_surface_id.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_pointer_enter(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
_serial: u32,
|
||||||
|
surface: &WlSurface,
|
||||||
|
surface_x: f64,
|
||||||
|
surface_y: f64,
|
||||||
|
) -> bool {
|
||||||
|
let surface_id = surface.id();
|
||||||
|
let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let position = active_surface.to_logical_position(surface_x, surface_y);
|
||||||
|
let window = active_surface.window_rc();
|
||||||
|
|
||||||
|
input_state.active_pointer_surface_id = Some(surface_id.clone());
|
||||||
|
input_state.current_pointer_position = position;
|
||||||
|
info!("Lock pointer enter on {:?}", surface_id);
|
||||||
|
window
|
||||||
|
.window()
|
||||||
|
.dispatch_event(WindowEvent::PointerMoved { position });
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_pointer_motion(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
surface_x: f64,
|
||||||
|
surface_y: f64,
|
||||||
|
) -> bool {
|
||||||
|
let Some(surface_id) = input_state.active_pointer_surface_id.clone() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let position = active_surface.to_logical_position(surface_x, surface_y);
|
||||||
|
let window = active_surface.window_rc();
|
||||||
|
|
||||||
|
input_state.current_pointer_position = position;
|
||||||
|
window
|
||||||
|
.window()
|
||||||
|
.dispatch_event(WindowEvent::PointerMoved { position });
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_pointer_leave(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
) -> bool {
|
||||||
|
let Some(surface_id) = input_state.active_pointer_surface_id.take() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) {
|
||||||
|
active_surface.dispatch_event(WindowEvent::PointerExited);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_pointer_button(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
scale_factor: f32,
|
||||||
|
_serial: u32,
|
||||||
|
button: u32,
|
||||||
|
button_state: WEnum<wl_pointer::ButtonState>,
|
||||||
|
) -> bool {
|
||||||
|
let Some(surface_id) = input_state.active_pointer_surface_id.clone() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let window = active_surface.window_rc();
|
||||||
|
let position = input_state.current_pointer_position;
|
||||||
|
let slint_button = wayland_button_to_slint(button);
|
||||||
|
let event = match button_state {
|
||||||
|
WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed {
|
||||||
|
button: slint_button,
|
||||||
|
position,
|
||||||
|
},
|
||||||
|
WEnum::Value(wl_pointer::ButtonState::Released) => WindowEvent::PointerReleased {
|
||||||
|
button: slint_button,
|
||||||
|
position,
|
||||||
|
},
|
||||||
|
_ => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Lock pointer button {:?} at {:?} (scale {})",
|
||||||
|
button_state, position, scale_factor
|
||||||
|
);
|
||||||
|
window.window().dispatch_event(event);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_axis_source(
|
||||||
|
input_state: &InputState,
|
||||||
|
_axis_source: wl_pointer::AxisSource,
|
||||||
|
) -> bool {
|
||||||
|
input_state.active_pointer_surface_id.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_axis(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
axis: wl_pointer::Axis,
|
||||||
|
value: f64,
|
||||||
|
) -> bool {
|
||||||
|
if input_state.active_pointer_surface_id.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match axis {
|
||||||
|
wl_pointer::Axis::HorizontalScroll => {
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
let delta = value as f32;
|
||||||
|
input_state.accumulated_axis_x += delta;
|
||||||
|
}
|
||||||
|
wl_pointer::Axis::VerticalScroll => {
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
let delta = value as f32;
|
||||||
|
input_state.accumulated_axis_y += delta;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_axis_discrete(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
axis: wl_pointer::Axis,
|
||||||
|
discrete: i32,
|
||||||
|
) -> bool {
|
||||||
|
if input_state.active_pointer_surface_id.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
let delta = (discrete as f32) * 60.0;
|
||||||
|
match axis {
|
||||||
|
wl_pointer::Axis::HorizontalScroll => {
|
||||||
|
input_state.accumulated_axis_x += delta;
|
||||||
|
}
|
||||||
|
wl_pointer::Axis::VerticalScroll => {
|
||||||
|
input_state.accumulated_axis_y += delta;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_axis_stop(input_state: &InputState, _axis: wl_pointer::Axis) -> bool {
|
||||||
|
input_state.active_pointer_surface_id.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_pointer_frame(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
) -> bool {
|
||||||
|
let Some(surface_id) = input_state.active_pointer_surface_id.clone() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let delta_x = input_state.accumulated_axis_x;
|
||||||
|
let delta_y = input_state.accumulated_axis_y;
|
||||||
|
input_state.accumulated_axis_x = 0.0;
|
||||||
|
input_state.accumulated_axis_y = 0.0;
|
||||||
|
|
||||||
|
let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let window = active_surface.window_rc();
|
||||||
|
|
||||||
|
if delta_x.abs() > f32::EPSILON || delta_y.abs() > f32::EPSILON {
|
||||||
|
let position = input_state.current_pointer_position;
|
||||||
|
window
|
||||||
|
.window()
|
||||||
|
.dispatch_event(WindowEvent::PointerScrolled {
|
||||||
|
position,
|
||||||
|
delta_x,
|
||||||
|
delta_y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_keyboard_enter(
|
||||||
|
input_state: &mut InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
surface: &WlSurface,
|
||||||
|
) -> bool {
|
||||||
|
let surface_id = surface.id();
|
||||||
|
if find_surface_by_surface_id(lock_surfaces, &surface_id).is_some() {
|
||||||
|
input_state.keyboard_focus_surface_id = Some(surface_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_keyboard_leave(input_state: &mut InputState, surface: &WlSurface) -> bool {
|
||||||
|
let surface_id = surface.id();
|
||||||
|
if input_state.keyboard_focus_surface_id.as_ref() == Some(&surface_id) {
|
||||||
|
input_state.keyboard_focus_surface_id = None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_keyboard_key(
|
||||||
|
input_state: &InputState,
|
||||||
|
lock_surfaces: &[(ObjectId, ActiveLockSurface)],
|
||||||
|
key: u32,
|
||||||
|
state: wl_keyboard::KeyState,
|
||||||
|
keyboard_state: &mut KeyboardState,
|
||||||
|
) -> bool {
|
||||||
|
let Some(surface_id) = input_state.keyboard_focus_surface_id.clone() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(active_surface) = find_surface_by_surface_id(lock_surfaces, &surface_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(xkb_state) = keyboard_state.xkb_state.as_mut() else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
let keycode = xkb::Keycode::new(key + 8);
|
||||||
|
let direction = match state {
|
||||||
|
wl_keyboard::KeyState::Pressed => xkb::KeyDirection::Down,
|
||||||
|
wl_keyboard::KeyState::Released => xkb::KeyDirection::Up,
|
||||||
|
_ => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
xkb_state.update_key(keycode, direction);
|
||||||
|
|
||||||
|
let text = xkb_state.key_get_utf8(keycode);
|
||||||
|
let text = if text.is_empty() {
|
||||||
|
let keysym = xkb_state.key_get_one_sym(keycode);
|
||||||
|
keysym_to_text(keysym)
|
||||||
|
} else {
|
||||||
|
Some(SharedString::from(text.as_str()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(text) = text else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = match state {
|
||||||
|
wl_keyboard::KeyState::Pressed => WindowEvent::KeyPressed { text },
|
||||||
|
wl_keyboard::KeyState::Released => WindowEvent::KeyReleased { text },
|
||||||
|
_ => return true,
|
||||||
|
};
|
||||||
|
info!("Lock key event {:?}", state);
|
||||||
|
active_surface.dispatch_event(event);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_surface_by_surface_id<'a>(
|
||||||
|
lock_surfaces: &'a [(ObjectId, ActiveLockSurface)],
|
||||||
|
surface_id: &ObjectId,
|
||||||
|
) -> Option<&'a ActiveLockSurface> {
|
||||||
|
lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.find(|(_, surface)| surface.surface().surface_id() == *surface_id)
|
||||||
|
.map(|(_, surface)| surface)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::errors::{LayerShikaError, Result};
|
||||||
|
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
||||||
|
use crate::rendering::femtovg::renderable_window::RenderableWindow;
|
||||||
|
use crate::wayland::session_lock::lock_context::SessionLockContext;
|
||||||
|
use slint::{
|
||||||
|
LogicalPosition, LogicalSize, WindowPosition, WindowSize, platform::WindowAdapter,
|
||||||
|
platform::femtovg_renderer::FemtoVGRenderer,
|
||||||
|
};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wayland_client::backend::ObjectId;
|
||||||
|
|
||||||
|
pub(super) fn create_window(
|
||||||
|
context: &SessionLockContext,
|
||||||
|
surface_id: &ObjectId,
|
||||||
|
scale_factor: f32,
|
||||||
|
) -> Result<Rc<FemtoVGWindow>> {
|
||||||
|
let init_size = LogicalSize::new(1.0, 1.0);
|
||||||
|
let render_context = context
|
||||||
|
.render_factory()
|
||||||
|
.create_context(surface_id, init_size.to_physical(scale_factor))?;
|
||||||
|
let renderer = FemtoVGRenderer::new(render_context)
|
||||||
|
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
||||||
|
let window = FemtoVGWindow::new(renderer);
|
||||||
|
RenderableWindow::set_scale_factor(window.as_ref(), scale_factor);
|
||||||
|
window.set_size(WindowSize::Logical(init_size));
|
||||||
|
window.set_position(WindowPosition::Logical(LogicalPosition::new(0., 0.)));
|
||||||
|
Ok(window)
|
||||||
|
}
|
||||||
397
crates/adapters/src/wayland/session_lock/manager/mod.rs
Normal file
397
crates/adapters/src/wayland/session_lock/manager/mod.rs
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
pub mod callbacks;
|
||||||
|
pub mod input_handling;
|
||||||
|
pub mod lifecycle;
|
||||||
|
pub mod rendering;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
use crate::errors::{LayerShikaError, Result};
|
||||||
|
use crate::rendering::slint_integration::platform::CustomSlintPlatform;
|
||||||
|
use crate::wayland::session_lock::lock_context::SessionLockContext;
|
||||||
|
use crate::wayland::session_lock::lock_surface::LockSurface;
|
||||||
|
use crate::wayland::surfaces::app_state::AppState;
|
||||||
|
use crate::wayland::surfaces::keyboard_state::KeyboardState;
|
||||||
|
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
||||||
|
use layer_shika_domain::value_objects::lock_state::LockState;
|
||||||
|
use log::info;
|
||||||
|
use slint_interpreter::{CompilationResult, ComponentDefinition, ComponentInstance};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wayland_client::{
|
||||||
|
Proxy, QueueHandle, WEnum,
|
||||||
|
backend::ObjectId,
|
||||||
|
protocol::{wl_keyboard, wl_output::WlOutput, wl_pointer, wl_surface::WlSurface},
|
||||||
|
};
|
||||||
|
use wayland_protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1;
|
||||||
|
|
||||||
|
pub use callbacks::{LockCallback, OutputFilter};
|
||||||
|
pub use state::{ActiveLockSurface, LockConfigureContext, LockSurfaceOutputContext};
|
||||||
|
|
||||||
|
use self::input_handling::InputState;
|
||||||
|
use crate::wayland::session_lock::lock_context::LockSurfaceParams;
|
||||||
|
|
||||||
|
pub struct SessionLockManager {
|
||||||
|
context: Rc<SessionLockContext>,
|
||||||
|
session_lock: Option<ExtSessionLockV1>,
|
||||||
|
lock_surfaces: Vec<(ObjectId, ActiveLockSurface)>,
|
||||||
|
state: LockState,
|
||||||
|
config: LockConfig,
|
||||||
|
component_definition: ComponentDefinition,
|
||||||
|
compilation_result: Option<Rc<CompilationResult>>,
|
||||||
|
platform: Rc<CustomSlintPlatform>,
|
||||||
|
callbacks: Vec<LockCallback>,
|
||||||
|
input_state: InputState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionLockManager {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
context: Rc<SessionLockContext>,
|
||||||
|
component_definition: ComponentDefinition,
|
||||||
|
compilation_result: Option<Rc<CompilationResult>>,
|
||||||
|
platform: Rc<CustomSlintPlatform>,
|
||||||
|
config: LockConfig,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
context,
|
||||||
|
session_lock: None,
|
||||||
|
lock_surfaces: Vec::new(),
|
||||||
|
state: LockState::Inactive,
|
||||||
|
config,
|
||||||
|
component_definition,
|
||||||
|
compilation_result,
|
||||||
|
platform,
|
||||||
|
callbacks: Vec::new(),
|
||||||
|
input_state: InputState::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn state(&self) -> LockState {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate(
|
||||||
|
&mut self,
|
||||||
|
outputs: impl IntoIterator<Item = WlOutput>,
|
||||||
|
queue_handle: &QueueHandle<AppState>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if !self.state.can_activate() {
|
||||||
|
return Err(LayerShikaError::InvalidInput {
|
||||||
|
message: format!("Session lock cannot activate in state {:?}", self.state),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config.validate()?;
|
||||||
|
|
||||||
|
let session_lock = self.context.lock_manager().lock(queue_handle, ());
|
||||||
|
self.session_lock = Some(session_lock.clone());
|
||||||
|
self.state = LockState::Locking;
|
||||||
|
|
||||||
|
for output in outputs {
|
||||||
|
let params = LockSurfaceParams {
|
||||||
|
compositor: self.context.compositor(),
|
||||||
|
output: &output,
|
||||||
|
session_lock: &session_lock,
|
||||||
|
fractional_scale_manager: self.context.fractional_scale_manager(),
|
||||||
|
viewporter: self.context.viewporter(),
|
||||||
|
queue_handle,
|
||||||
|
};
|
||||||
|
let surface = LockSurface::create(¶ms);
|
||||||
|
let surface_id = surface.surface_id();
|
||||||
|
let window = lifecycle::create_window(
|
||||||
|
&self.context,
|
||||||
|
&surface_id,
|
||||||
|
self.config.scale_factor.value(),
|
||||||
|
)?;
|
||||||
|
self.lock_surfaces
|
||||||
|
.push((output.id(), ActiveLockSurface::new(surface, window)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_locked(&mut self) {
|
||||||
|
if self.state == LockState::Locking {
|
||||||
|
info!("Session lock transitioned to Locked");
|
||||||
|
self.state = LockState::Locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deactivate(&mut self) -> Result<()> {
|
||||||
|
if !self.state.can_deactivate() {
|
||||||
|
return Err(LayerShikaError::InvalidInput {
|
||||||
|
message: format!("Session lock cannot deactivate in state {:?}", self.state),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(session_lock) = self.session_lock.take() else {
|
||||||
|
return Err(LayerShikaError::InvalidInput {
|
||||||
|
message: "Session lock object missing during deactivate".to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (_, surface) in &self.lock_surfaces {
|
||||||
|
surface.surface().destroy();
|
||||||
|
}
|
||||||
|
session_lock.unlock_and_destroy();
|
||||||
|
self.lock_surfaces.clear();
|
||||||
|
self.input_state.reset();
|
||||||
|
self.state = LockState::Unlocking;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_finished(&mut self) {
|
||||||
|
info!("Session lock finished");
|
||||||
|
self.lock_surfaces.clear();
|
||||||
|
self.session_lock = None;
|
||||||
|
self.state = LockState::Inactive;
|
||||||
|
self.input_state.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_output(
|
||||||
|
&mut self,
|
||||||
|
output: &WlOutput,
|
||||||
|
queue_handle: &QueueHandle<AppState>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if self.state != LockState::Locked {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let output_id = output.id();
|
||||||
|
if self.lock_surfaces.iter().any(|(id, _)| *id == output_id) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(session_lock) = self.session_lock.as_ref() else {
|
||||||
|
return Err(LayerShikaError::InvalidInput {
|
||||||
|
message: "Session lock object missing during output hotplug".to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("Adding lock surface for output {output_id:?}");
|
||||||
|
let params = LockSurfaceParams {
|
||||||
|
compositor: self.context.compositor(),
|
||||||
|
output,
|
||||||
|
session_lock,
|
||||||
|
fractional_scale_manager: self.context.fractional_scale_manager(),
|
||||||
|
viewporter: self.context.viewporter(),
|
||||||
|
queue_handle,
|
||||||
|
};
|
||||||
|
let surface = LockSurface::create(¶ms);
|
||||||
|
let surface_id = surface.surface_id();
|
||||||
|
let window =
|
||||||
|
lifecycle::create_window(&self.context, &surface_id, self.config.scale_factor.value())?;
|
||||||
|
self.lock_surfaces
|
||||||
|
.push((output_id, ActiveLockSurface::new(surface, window)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_output(&mut self, output_id: &ObjectId) {
|
||||||
|
if let Some(pos) = self
|
||||||
|
.lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.position(|(id, _)| id == output_id)
|
||||||
|
{
|
||||||
|
let (_, surface) = self.lock_surfaces.remove(pos);
|
||||||
|
let surface_id = surface.surface().surface_id();
|
||||||
|
self.input_state.clear_surface_refs(&surface_id);
|
||||||
|
drop(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_surface_by_lock_surface_id_mut(
|
||||||
|
&mut self,
|
||||||
|
lock_surface_id: &ObjectId,
|
||||||
|
) -> Option<&mut ActiveLockSurface> {
|
||||||
|
self.lock_surfaces
|
||||||
|
.iter_mut()
|
||||||
|
.find(|(_, surface)| surface.surface().lock_surface_id() == *lock_surface_id)
|
||||||
|
.map(|(_, surface)| surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_surface_by_surface_id(&self, surface_id: &ObjectId) -> Option<&ActiveLockSurface> {
|
||||||
|
self.lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.find(|(_, surface)| surface.surface().surface_id() == *surface_id)
|
||||||
|
.map(|(_, surface)| surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_output_id_for_lock_surface(&self, lock_surface_id: &ObjectId) -> Option<ObjectId> {
|
||||||
|
self.lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.find(|(_, surface)| surface.surface().surface_id() == *lock_surface_id)
|
||||||
|
.map(|(id, _)| id.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_configure(
|
||||||
|
&mut self,
|
||||||
|
lock_surface_id: &ObjectId,
|
||||||
|
serial: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
output_ctx: LockSurfaceOutputContext,
|
||||||
|
) -> Result<()> {
|
||||||
|
let component_name = self.component_definition.name().to_string();
|
||||||
|
|
||||||
|
let context = LockConfigureContext {
|
||||||
|
scale_factor: self.config.scale_factor.value(),
|
||||||
|
component_definition: self.component_definition.clone(),
|
||||||
|
compilation_result: self.compilation_result.clone(),
|
||||||
|
platform: Rc::clone(&self.platform),
|
||||||
|
callbacks: self.callbacks.clone(),
|
||||||
|
component_name,
|
||||||
|
output_handle: output_ctx.output_handle,
|
||||||
|
output_info: output_ctx.output_info,
|
||||||
|
primary_handle: output_ctx.primary_handle,
|
||||||
|
active_handle: output_ctx.active_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(surface) = self.find_surface_by_lock_surface_id_mut(lock_surface_id) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
surface.handle_configure(serial, width, height, &context)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_frames(&self) -> Result<()> {
|
||||||
|
rendering::render_frames(&self.lock_surfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn register_callback(&mut self, callback: LockCallback) {
|
||||||
|
for (_, surface) in &self.lock_surfaces {
|
||||||
|
surface.apply_callback(&callback);
|
||||||
|
}
|
||||||
|
self.callbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_fractional_scale(&mut self, fractional_scale_id: &ObjectId, scale_120ths: u32) {
|
||||||
|
for (_, surface) in &mut self.lock_surfaces {
|
||||||
|
let matches = surface
|
||||||
|
.surface()
|
||||||
|
.fractional_scale()
|
||||||
|
.is_some_and(|fs| fs.id() == *fractional_scale_id);
|
||||||
|
if matches {
|
||||||
|
surface.handle_fractional_scale(scale_120ths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_lock_surface(&self, surface_id: &ObjectId) -> bool {
|
||||||
|
self.find_surface_by_surface_id(surface_id).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn has_active_pointer(&self) -> bool {
|
||||||
|
self.input_state.has_active_pointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn has_keyboard_focus(&self) -> bool {
|
||||||
|
self.input_state.has_keyboard_focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pointer_enter(
|
||||||
|
&mut self,
|
||||||
|
serial: u32,
|
||||||
|
surface: &WlSurface,
|
||||||
|
surface_x: f64,
|
||||||
|
surface_y: f64,
|
||||||
|
) -> bool {
|
||||||
|
input_handling::handle_pointer_enter(
|
||||||
|
&mut self.input_state,
|
||||||
|
&self.lock_surfaces,
|
||||||
|
serial,
|
||||||
|
surface,
|
||||||
|
surface_x,
|
||||||
|
surface_y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pointer_motion(&mut self, surface_x: f64, surface_y: f64) -> bool {
|
||||||
|
input_handling::handle_pointer_motion(
|
||||||
|
&mut self.input_state,
|
||||||
|
&self.lock_surfaces,
|
||||||
|
surface_x,
|
||||||
|
surface_y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pointer_leave(&mut self) -> bool {
|
||||||
|
input_handling::handle_pointer_leave(&mut self.input_state, &self.lock_surfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pointer_button(
|
||||||
|
&mut self,
|
||||||
|
serial: u32,
|
||||||
|
button: u32,
|
||||||
|
button_state: WEnum<wl_pointer::ButtonState>,
|
||||||
|
) -> bool {
|
||||||
|
input_handling::handle_pointer_button(
|
||||||
|
&mut self.input_state,
|
||||||
|
&self.lock_surfaces,
|
||||||
|
self.config.scale_factor.value(),
|
||||||
|
serial,
|
||||||
|
button,
|
||||||
|
button_state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_axis_source(&mut self, axis_source: wl_pointer::AxisSource) -> bool {
|
||||||
|
input_handling::handle_axis_source(&self.input_state, axis_source)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_axis(&mut self, axis: wl_pointer::Axis, value: f64) -> bool {
|
||||||
|
input_handling::handle_axis(&mut self.input_state, axis, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_axis_discrete(&mut self, axis: wl_pointer::Axis, discrete: i32) -> bool {
|
||||||
|
input_handling::handle_axis_discrete(&mut self.input_state, axis, discrete)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_axis_stop(&mut self, axis: wl_pointer::Axis) -> bool {
|
||||||
|
input_handling::handle_axis_stop(&self.input_state, axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pointer_frame(&mut self) -> bool {
|
||||||
|
input_handling::handle_pointer_frame(&mut self.input_state, &self.lock_surfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_keyboard_enter(&mut self, surface: &WlSurface) -> bool {
|
||||||
|
input_handling::handle_keyboard_enter(&mut self.input_state, &self.lock_surfaces, surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_keyboard_leave(&mut self, surface: &WlSurface) -> bool {
|
||||||
|
input_handling::handle_keyboard_leave(&mut self.input_state, surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_keyboard_key(
|
||||||
|
&mut self,
|
||||||
|
key: u32,
|
||||||
|
state: wl_keyboard::KeyState,
|
||||||
|
keyboard_state: &mut KeyboardState,
|
||||||
|
) -> bool {
|
||||||
|
input_handling::handle_keyboard_key(
|
||||||
|
&self.input_state,
|
||||||
|
&self.lock_surfaces,
|
||||||
|
key,
|
||||||
|
state,
|
||||||
|
keyboard_state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_lock_surfaces(&self, f: &mut dyn FnMut(&ObjectId, &ComponentInstance)) {
|
||||||
|
for (output_id, active_surface) in &self.lock_surfaces {
|
||||||
|
if let Some(component) = active_surface.component() {
|
||||||
|
f(output_id, component.component_instance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn component_name(&self) -> &ComponentDefinition {
|
||||||
|
&self.component_definition
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_lock_surfaces(&self) -> usize {
|
||||||
|
self.lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, s)| s.component().is_some())
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::errors::Result;
|
||||||
|
use wayland_client::backend::ObjectId;
|
||||||
|
|
||||||
|
use super::state::ActiveLockSurface;
|
||||||
|
|
||||||
|
pub(super) fn render_frames(lock_surfaces: &[(ObjectId, ActiveLockSurface)]) -> Result<()> {
|
||||||
|
for (_, surface) in lock_surfaces {
|
||||||
|
surface.render_frame_if_dirty()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
248
crates/adapters/src/wayland/session_lock/manager/state.rs
Normal file
248
crates/adapters/src/wayland/session_lock/manager/state.rs
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
use crate::errors::Result;
|
||||||
|
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
||||||
|
use crate::rendering::femtovg::renderable_window::RenderableWindow;
|
||||||
|
use crate::rendering::slint_integration::platform::CustomSlintPlatform;
|
||||||
|
use crate::wayland::session_lock::lock_surface::LockSurface;
|
||||||
|
use crate::wayland::surfaces::component_state::ComponentState;
|
||||||
|
use crate::wayland::surfaces::display_metrics::DisplayMetrics;
|
||||||
|
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
||||||
|
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||||
|
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||||
|
use log::info;
|
||||||
|
use slint::{
|
||||||
|
LogicalPosition, LogicalSize, WindowSize,
|
||||||
|
platform::{WindowAdapter, WindowEvent},
|
||||||
|
};
|
||||||
|
use slint_interpreter::{CompilationResult, ComponentDefinition};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::callbacks::LockCallback;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum LockScalingMode {
|
||||||
|
FractionalWithViewport,
|
||||||
|
FractionalOnly,
|
||||||
|
Integer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LockSurfaceOutputContext {
|
||||||
|
pub output_handle: OutputHandle,
|
||||||
|
pub output_info: Option<OutputInfo>,
|
||||||
|
pub primary_handle: Option<OutputHandle>,
|
||||||
|
pub active_handle: Option<OutputHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LockConfigureContext {
|
||||||
|
pub scale_factor: f32,
|
||||||
|
pub component_definition: ComponentDefinition,
|
||||||
|
pub compilation_result: Option<Rc<CompilationResult>>,
|
||||||
|
pub platform: Rc<CustomSlintPlatform>,
|
||||||
|
pub callbacks: Vec<LockCallback>,
|
||||||
|
pub component_name: String,
|
||||||
|
pub output_handle: OutputHandle,
|
||||||
|
pub output_info: Option<OutputInfo>,
|
||||||
|
pub primary_handle: Option<OutputHandle>,
|
||||||
|
pub active_handle: Option<OutputHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ActiveLockSurface {
|
||||||
|
surface: LockSurface,
|
||||||
|
window: Rc<FemtoVGWindow>,
|
||||||
|
component: Option<ComponentState>,
|
||||||
|
scale_factor: f32,
|
||||||
|
has_fractional_scale: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveLockSurface {
|
||||||
|
pub fn new(surface: LockSurface, window: Rc<FemtoVGWindow>) -> Self {
|
||||||
|
Self {
|
||||||
|
has_fractional_scale: surface.fractional_scale().is_some(),
|
||||||
|
surface,
|
||||||
|
window,
|
||||||
|
component: None,
|
||||||
|
scale_factor: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_configure(
|
||||||
|
&mut self,
|
||||||
|
serial: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
context: &LockConfigureContext,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.surface.handle_configure(serial, width, height);
|
||||||
|
self.scale_factor = context.scale_factor;
|
||||||
|
let dimensions = match SurfaceDimensions::calculate(width, height, context.scale_factor) {
|
||||||
|
Ok(dimensions) => dimensions,
|
||||||
|
Err(err) => {
|
||||||
|
info!("Failed to calculate lock surface dimensions: {err}");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let scaling_mode = self.scaling_mode();
|
||||||
|
info!(
|
||||||
|
"Lock surface dimensions: logical {}x{}, physical {}x{}, scale {}, mode {:?}",
|
||||||
|
dimensions.logical_width(),
|
||||||
|
dimensions.logical_height(),
|
||||||
|
dimensions.physical_width(),
|
||||||
|
dimensions.physical_height(),
|
||||||
|
context.scale_factor,
|
||||||
|
scaling_mode
|
||||||
|
);
|
||||||
|
self.configure_window(&dimensions, scaling_mode, context.scale_factor);
|
||||||
|
self.configure_surface(&dimensions, scaling_mode);
|
||||||
|
|
||||||
|
if self.component.is_none() {
|
||||||
|
context.platform.add_window(Rc::clone(&self.window));
|
||||||
|
let component = ComponentState::new(
|
||||||
|
context.component_definition.clone(),
|
||||||
|
context.compilation_result.clone(),
|
||||||
|
&self.window,
|
||||||
|
)?;
|
||||||
|
self.window
|
||||||
|
.window()
|
||||||
|
.dispatch_event(WindowEvent::WindowActiveChanged(true));
|
||||||
|
for callback in &context.callbacks {
|
||||||
|
if callback.should_apply(
|
||||||
|
&context.component_name,
|
||||||
|
context.output_handle,
|
||||||
|
context.output_info.as_ref(),
|
||||||
|
context.primary_handle,
|
||||||
|
context.active_handle,
|
||||||
|
) {
|
||||||
|
if let Err(err) = callback.apply_to(component.component_instance()) {
|
||||||
|
info!(
|
||||||
|
"Failed to register lock callback '{}': {err}",
|
||||||
|
callback.name()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
info!("Registered lock callback '{}'", callback.name());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Skipping callback '{}' due to selector filter (output {:?})",
|
||||||
|
callback.name(),
|
||||||
|
context.output_handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.component = Some(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderableWindow::request_redraw(self.window.as_ref());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_frame_if_dirty(&self) -> Result<()> {
|
||||||
|
self.window.render_frame_if_dirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_fractional_scale(&mut self, scale_120ths: u32) {
|
||||||
|
let scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths);
|
||||||
|
self.scale_factor = scale_factor;
|
||||||
|
if self.surface.width() == 0 || self.surface.height() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Ok(dimensions) =
|
||||||
|
SurfaceDimensions::calculate(self.surface.width(), self.surface.height(), scale_factor)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let scaling_mode = self.scaling_mode();
|
||||||
|
self.configure_window(&dimensions, scaling_mode, scale_factor);
|
||||||
|
self.configure_surface(&dimensions, scaling_mode);
|
||||||
|
RenderableWindow::request_redraw(self.window.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_callback(&self, callback: &LockCallback) {
|
||||||
|
if let Some(component) = self.component.as_ref() {
|
||||||
|
if let Err(err) = callback.apply_to(component.component_instance()) {
|
||||||
|
info!(
|
||||||
|
"Failed to register lock callback '{}': {err}",
|
||||||
|
callback.name()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scaling_mode(&self) -> LockScalingMode {
|
||||||
|
if self.surface.has_fractional_scale() && self.surface.has_viewport() {
|
||||||
|
LockScalingMode::FractionalWithViewport
|
||||||
|
} else if self.surface.has_fractional_scale() {
|
||||||
|
LockScalingMode::FractionalOnly
|
||||||
|
} else {
|
||||||
|
LockScalingMode::Integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
fn configure_window(
|
||||||
|
&self,
|
||||||
|
dimensions: &SurfaceDimensions,
|
||||||
|
mode: LockScalingMode,
|
||||||
|
scale_factor: f32,
|
||||||
|
) {
|
||||||
|
match mode {
|
||||||
|
LockScalingMode::FractionalWithViewport | LockScalingMode::FractionalOnly => {
|
||||||
|
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
|
||||||
|
self.window.set_size(WindowSize::Logical(LogicalSize::new(
|
||||||
|
dimensions.logical_width() as f32,
|
||||||
|
dimensions.logical_height() as f32,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
LockScalingMode::Integer => {
|
||||||
|
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
|
||||||
|
self.window
|
||||||
|
.set_size(WindowSize::Physical(slint::PhysicalSize::new(
|
||||||
|
dimensions.physical_width(),
|
||||||
|
dimensions.physical_height(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_surface(&self, dimensions: &SurfaceDimensions, mode: LockScalingMode) {
|
||||||
|
match mode {
|
||||||
|
LockScalingMode::FractionalWithViewport => {
|
||||||
|
self.surface.configure_fractional_viewport(
|
||||||
|
dimensions.logical_width(),
|
||||||
|
dimensions.logical_height(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
LockScalingMode::FractionalOnly | LockScalingMode::Integer => {
|
||||||
|
self.surface
|
||||||
|
.configure_buffer_scale(dimensions.buffer_scale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
pub fn to_logical_position(&self, surface_x: f64, surface_y: f64) -> LogicalPosition {
|
||||||
|
if self.has_fractional_scale {
|
||||||
|
let x = surface_x as f32;
|
||||||
|
let y = surface_y as f32;
|
||||||
|
LogicalPosition::new(x, y)
|
||||||
|
} else {
|
||||||
|
let x = (surface_x / f64::from(self.scale_factor)) as f32;
|
||||||
|
let y = (surface_y / f64::from(self.scale_factor)) as f32;
|
||||||
|
LogicalPosition::new(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_event(&self, event: WindowEvent) {
|
||||||
|
self.window.window().dispatch_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_rc(&self) -> Rc<FemtoVGWindow> {
|
||||||
|
Rc::clone(&self.window)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn surface(&self) -> &LockSurface {
|
||||||
|
&self.surface
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn component(&self) -> Option<&ComponentState> {
|
||||||
|
self.component.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
pub mod lock_context;
|
pub mod lock_context;
|
||||||
pub mod lock_manager;
|
|
||||||
pub mod lock_surface;
|
pub mod lock_surface;
|
||||||
|
pub mod manager;
|
||||||
|
|
||||||
|
pub use manager::{LockCallback, LockSurfaceOutputContext, OutputFilter, SessionLockManager};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::wayland::{
|
||||||
managed_proxies::{ManagedWlKeyboard, ManagedWlPointer},
|
managed_proxies::{ManagedWlKeyboard, ManagedWlPointer},
|
||||||
ops::WaylandSystemOps,
|
ops::WaylandSystemOps,
|
||||||
outputs::{OutputManager, OutputManagerContext},
|
outputs::{OutputManager, OutputManagerContext},
|
||||||
session_lock::lock_manager::OutputFilter,
|
session_lock::OutputFilter,
|
||||||
surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
|
surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
|
||||||
surfaces::popup_manager::{PopupContext, PopupManager},
|
surfaces::popup_manager::{PopupContext, PopupManager},
|
||||||
surfaces::{
|
surfaces::{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::wayland::globals::context::GlobalContext;
|
||||||
use crate::wayland::managed_proxies::{ManagedWlKeyboard, ManagedWlPointer};
|
use crate::wayland::managed_proxies::{ManagedWlKeyboard, ManagedWlPointer};
|
||||||
use crate::wayland::outputs::{OutputManager, OutputMapping};
|
use crate::wayland::outputs::{OutputManager, OutputMapping};
|
||||||
use crate::wayland::session_lock::lock_context::SessionLockContext;
|
use crate::wayland::session_lock::lock_context::SessionLockContext;
|
||||||
use crate::wayland::session_lock::lock_manager::{LockCallback, OutputFilter, SessionLockManager};
|
use crate::wayland::session_lock::{LockCallback, OutputFilter, SessionLockManager};
|
||||||
use layer_shika_domain::entities::output_registry::OutputRegistry;
|
use layer_shika_domain::entities::output_registry::OutputRegistry;
|
||||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||||
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue