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::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::surfaces::app_state::AppState;
|
||||
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::display_metrics::DisplayMetrics;
|
||||
use crate::wayland::surfaces::surface_state::SurfaceState;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::errors::Result;
|
||||
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 layer_shika_domain::value_objects::lock_config::LockConfig;
|
||||
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_manager;
|
||||
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},
|
||||
ops::WaylandSystemOps,
|
||||
outputs::{OutputManager, OutputManagerContext},
|
||||
session_lock::lock_manager::OutputFilter,
|
||||
session_lock::OutputFilter,
|
||||
surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
|
||||
surfaces::popup_manager::{PopupContext, PopupManager},
|
||||
surfaces::{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::wayland::globals::context::GlobalContext;
|
|||
use crate::wayland::managed_proxies::{ManagedWlKeyboard, ManagedWlPointer};
|
||||
use crate::wayland::outputs::{OutputManager, OutputMapping};
|
||||
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::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::lock_config::LockConfig;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue