mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2026-01-22 07:05:56 +00:00
feat: add minimal mode for lock surfaces only
This commit is contained in:
parent
e6f425842b
commit
e025cb008c
7 changed files with 155 additions and 39 deletions
|
|
@ -23,6 +23,14 @@ impl CustomSlintPlatform {
|
|||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn new_empty() -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
pending_windows: RefCell::new(Vec::new()),
|
||||
popup_creator: OnceCell::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_window(&self, window: Rc<FemtoVGWindow>) {
|
||||
self.pending_windows.borrow_mut().push(window);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::wayland::surfaces::app_state::AppState;
|
|||
pub struct GlobalContext {
|
||||
pub compositor: WlCompositor,
|
||||
pub outputs: Vec<WlOutput>,
|
||||
pub layer_shell: ZwlrLayerShellV1,
|
||||
pub layer_shell: Option<ZwlrLayerShellV1>,
|
||||
pub seat: WlSeat,
|
||||
pub xdg_wm_base: Option<XdgWmBase>,
|
||||
pub session_lock_manager: Option<ExtSessionLockManagerV1>,
|
||||
|
|
@ -38,14 +38,17 @@ impl GlobalContext {
|
|||
.map(|(global_list, _)| global_list)
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization { source: e })?;
|
||||
|
||||
let (compositor, layer_shell, seat) = bind_globals!(
|
||||
let (compositor, seat) = bind_globals!(
|
||||
&global_list,
|
||||
queue_handle,
|
||||
(WlCompositor, compositor, 3..=6),
|
||||
(ZwlrLayerShellV1, layer_shell, 1..=5),
|
||||
(WlSeat, seat, 1..=9)
|
||||
)?;
|
||||
|
||||
let layer_shell = global_list
|
||||
.bind::<ZwlrLayerShellV1, _, _>(queue_handle, 1..=5, ())
|
||||
.ok();
|
||||
|
||||
let output_names: Vec<u32> = global_list
|
||||
.contents()
|
||||
.clone_list()
|
||||
|
|
@ -115,6 +118,10 @@ impl GlobalContext {
|
|||
info!("Viewporter protocol not available");
|
||||
}
|
||||
|
||||
if layer_shell.is_none() {
|
||||
info!("wlr-layer-shell protocol not available, layer surfaces disabled");
|
||||
}
|
||||
|
||||
let render_context_manager = RenderContextManager::new(&connection.display().id())?;
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ 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;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use slint_interpreter::ComponentInstance;
|
||||
use slint_interpreter::{ComponentInstance, CompilationResult};
|
||||
use slint_interpreter::Value;
|
||||
use smithay_client_toolkit::reexports::calloop::LoopHandle;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -19,6 +19,8 @@ pub trait WaylandSystemOps {
|
|||
|
||||
fn despawn_surface(&mut self, name: &str) -> Result<()>;
|
||||
|
||||
fn set_compilation_result(&mut self, compilation_result: Rc<CompilationResult>);
|
||||
|
||||
fn activate_session_lock(&mut self, component_name: &str, config: LockConfig) -> Result<()>;
|
||||
|
||||
fn deactivate_session_lock(&mut self) -> Result<()>;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ use super::OutputMapping;
|
|||
|
||||
pub struct OutputManagerContext {
|
||||
pub compositor: WlCompositor,
|
||||
pub layer_shell: ZwlrLayerShellV1,
|
||||
pub layer_shell: Option<ZwlrLayerShellV1>,
|
||||
pub fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
|
||||
pub viewporter: Option<WpViewporter>,
|
||||
pub render_factory: Rc<RenderContextFactory>,
|
||||
|
|
@ -161,10 +161,16 @@ impl OutputManager {
|
|||
_output_id: &ObjectId,
|
||||
queue_handle: &QueueHandle<AppState>,
|
||||
) -> Result<(SurfaceState, ObjectId)> {
|
||||
let layer_shell = self.context.layer_shell.as_ref().ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "wlr-layer-shell protocol not available - cannot create layer surfaces".into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let setup_params = SurfaceSetupParams {
|
||||
compositor: &self.context.compositor,
|
||||
output,
|
||||
layer_shell: &self.context.layer_shell,
|
||||
layer_shell,
|
||||
fractional_scale_manager: self.context.fractional_scale_manager.as_ref(),
|
||||
viewporter: self.context.viewporter.as_ref(),
|
||||
queue_handle,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use slint::{
|
|||
LogicalPosition, PhysicalSize, PlatformError, WindowPosition,
|
||||
platform::{WindowAdapter, femtovg_renderer::FemtoVGRenderer, set_platform, update_timers_and_animations},
|
||||
};
|
||||
use slint_interpreter::ComponentInstance;
|
||||
use slint_interpreter::{ComponentInstance, CompilationResult};
|
||||
use smithay_client_toolkit::reexports::calloop::{
|
||||
EventLoop, Interest, LoopHandle, Mode, PostAction, generic::Generic,
|
||||
};
|
||||
|
|
@ -97,9 +97,7 @@ impl WaylandShellSystem {
|
|||
|
||||
pub fn new_multi(configs: &[ShellSurfaceConfig]) -> Result<Self> {
|
||||
if configs.is_empty() {
|
||||
return Err(LayerShikaError::InvalidInput {
|
||||
message: "At least one surface config is required".into(),
|
||||
});
|
||||
return Self::new_minimal();
|
||||
}
|
||||
|
||||
info!(
|
||||
|
|
@ -120,6 +118,22 @@ impl WaylandShellSystem {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn new_minimal() -> Result<Self> {
|
||||
info!("Initializing WindowingSystem in minimal mode (no layer surfaces)");
|
||||
let (connection, mut event_queue) = Self::init_wayland_connection()?;
|
||||
let event_loop =
|
||||
EventLoop::try_new().map_err(|e| EventLoopError::Creation { source: e })?;
|
||||
|
||||
let state = Self::init_state_minimal(&connection, &mut event_queue)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
connection,
|
||||
event_queue,
|
||||
event_loop,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_wayland_connection() -> Result<(Rc<Connection>, EventQueue<AppState>)> {
|
||||
let connection = Rc::new(Connection::connect_to_env()?);
|
||||
let event_queue = connection.new_event_queue();
|
||||
|
|
@ -145,6 +159,12 @@ impl WaylandShellSystem {
|
|||
pointer: &Rc<WlPointer>,
|
||||
layer_surface_config: &LayerSurfaceConfig,
|
||||
) -> Result<Vec<OutputSetup>> {
|
||||
let layer_shell = global_ctx.layer_shell.as_ref().ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "wlr-layer-shell protocol not available - cannot create layer surfaces".into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let mut setups = Vec::new();
|
||||
|
||||
for (index, output) in global_ctx.outputs.iter().enumerate() {
|
||||
|
|
@ -163,7 +183,7 @@ impl WaylandShellSystem {
|
|||
let setup_params = SurfaceSetupParams {
|
||||
compositor: &global_ctx.compositor,
|
||||
output,
|
||||
layer_shell: &global_ctx.layer_shell,
|
||||
layer_shell,
|
||||
fractional_scale_manager: global_ctx.fractional_scale_manager.as_ref(),
|
||||
viewporter: global_ctx.viewporter.as_ref(),
|
||||
queue_handle: &event_queue.handle(),
|
||||
|
|
@ -426,6 +446,38 @@ impl WaylandShellSystem {
|
|||
Ok(app_state)
|
||||
}
|
||||
|
||||
fn init_state_minimal(
|
||||
connection: &Connection,
|
||||
event_queue: &mut EventQueue<AppState>,
|
||||
) -> Result<AppState> {
|
||||
let global_ctx = Rc::new(GlobalContext::initialize(
|
||||
connection,
|
||||
&event_queue.handle(),
|
||||
)?);
|
||||
|
||||
let pointer = Rc::new(global_ctx.seat.get_pointer(&event_queue.handle(), ()));
|
||||
let keyboard = Rc::new(global_ctx.seat.get_keyboard(&event_queue.handle(), ()));
|
||||
let shared_serial = Rc::new(SharedPointerSerial::new());
|
||||
|
||||
let mut app_state = AppState::new(
|
||||
ManagedWlPointer::new(Rc::clone(&pointer), Rc::new(connection.clone())),
|
||||
ManagedWlKeyboard::new(Rc::clone(&keyboard), Rc::new(connection.clone())),
|
||||
Rc::clone(&shared_serial),
|
||||
);
|
||||
|
||||
app_state.set_queue_handle(event_queue.handle());
|
||||
app_state.set_global_context(Rc::clone(&global_ctx));
|
||||
|
||||
let platform = CustomSlintPlatform::new_empty();
|
||||
set_platform(Box::new(PlatformWrapper(Rc::clone(&platform))))
|
||||
.map_err(|e| LayerShikaError::PlatformSetup { source: e })?;
|
||||
app_state.set_slint_platform(Rc::clone(&platform));
|
||||
|
||||
info!("Minimal state initialized successfully (no layer surfaces, empty Slint platform for session locks)");
|
||||
|
||||
Ok(app_state)
|
||||
}
|
||||
|
||||
fn create_output_setups_multi(
|
||||
configs: &[ShellSurfaceConfig],
|
||||
global_ctx: &GlobalContext,
|
||||
|
|
@ -433,6 +485,12 @@ impl WaylandShellSystem {
|
|||
event_queue: &mut EventQueue<AppState>,
|
||||
pointer: &Rc<WlPointer>,
|
||||
) -> Result<Vec<OutputSetup>> {
|
||||
let layer_shell = global_ctx.layer_shell.as_ref().ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "wlr-layer-shell protocol not available - cannot create layer surfaces".into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let mut setups = Vec::new();
|
||||
|
||||
for (output_index, output) in global_ctx.outputs.iter().enumerate() {
|
||||
|
|
@ -458,7 +516,7 @@ impl WaylandShellSystem {
|
|||
let setup_params = SurfaceSetupParams {
|
||||
compositor: &global_ctx.compositor,
|
||||
output,
|
||||
layer_shell: &global_ctx.layer_shell,
|
||||
layer_shell,
|
||||
fractional_scale_manager: global_ctx.fractional_scale_manager.as_ref(),
|
||||
viewporter: global_ctx.viewporter.as_ref(),
|
||||
queue_handle: &event_queue.handle(),
|
||||
|
|
@ -805,6 +863,10 @@ impl WaylandSystemOps for WaylandShellSystem {
|
|||
WaylandShellSystem::despawn_surface(self, name)
|
||||
}
|
||||
|
||||
fn set_compilation_result(&mut self, compilation_result: Rc<CompilationResult>) {
|
||||
self.state.set_compilation_result(compilation_result);
|
||||
}
|
||||
|
||||
fn activate_session_lock(&mut self, component_name: &str, config: LockConfig) -> Result<()> {
|
||||
self.state.activate_session_lock(component_name, config)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ pub struct AppState {
|
|||
global_context: Option<Rc<GlobalContext>>,
|
||||
known_outputs: Vec<WlOutput>,
|
||||
slint_platform: Option<Rc<CustomSlintPlatform>>,
|
||||
compilation_result: Option<Rc<CompilationResult>>,
|
||||
output_registry: OutputRegistry,
|
||||
output_mapping: OutputMapping,
|
||||
surfaces: HashMap<ShellSurfaceKey, PerOutputSurface>,
|
||||
|
|
@ -83,6 +84,7 @@ impl AppState {
|
|||
global_context: None,
|
||||
known_outputs: Vec::new(),
|
||||
slint_platform: None,
|
||||
compilation_result: None,
|
||||
output_registry: OutputRegistry::new(),
|
||||
output_mapping: OutputMapping::new(),
|
||||
surfaces: HashMap::new(),
|
||||
|
|
@ -112,6 +114,10 @@ impl AppState {
|
|||
self.slint_platform = Some(platform);
|
||||
}
|
||||
|
||||
pub fn set_compilation_result(&mut self, compilation_result: Rc<CompilationResult>) {
|
||||
self.compilation_result = Some(compilation_result);
|
||||
}
|
||||
|
||||
pub fn set_queue_handle(&mut self, queue_handle: wayland_client::QueueHandle<AppState>) {
|
||||
self.queue_handle = Some(queue_handle);
|
||||
}
|
||||
|
|
@ -255,8 +261,12 @@ impl AppState {
|
|||
component_name: &str,
|
||||
) -> Result<(ComponentDefinition, Option<Rc<CompilationResult>>)> {
|
||||
let compilation_result = self
|
||||
.primary_output()
|
||||
.and_then(SurfaceState::compilation_result)
|
||||
.compilation_result
|
||||
.clone()
|
||||
.or_else(|| {
|
||||
self.primary_output()
|
||||
.and_then(SurfaceState::compilation_result)
|
||||
})
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "No compilation result available for session lock".to_string(),
|
||||
})?;
|
||||
|
|
|
|||
|
|
@ -77,15 +77,11 @@ impl ShellBuilder {
|
|||
}
|
||||
|
||||
/// Builds the shell from the configured surfaces
|
||||
///
|
||||
/// If no surfaces are configured, creates a minimal shell without layer surfaces.
|
||||
/// This is useful for lock-only applications that don't need persistent UI surfaces.
|
||||
pub fn build(self) -> Result<Shell> {
|
||||
let surfaces = if self.surfaces.is_empty() {
|
||||
vec![SurfaceDefinition {
|
||||
component: DEFAULT_COMPONENT_NAME.to_string(),
|
||||
config: SurfaceConfig::default(),
|
||||
}]
|
||||
} else {
|
||||
self.surfaces
|
||||
};
|
||||
let surfaces = self.surfaces;
|
||||
|
||||
let compilation_result = match self.compilation {
|
||||
CompilationSource::File { path, compiler } => {
|
||||
|
|
@ -399,27 +395,24 @@ impl Shell {
|
|||
) -> Result<Self> {
|
||||
log::info!("Creating Shell with {} windows", definitions.len());
|
||||
|
||||
if definitions.is_empty() {
|
||||
return Err(Error::Domain(DomainError::Configuration {
|
||||
message: "At least one window definition is required".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
for def in &definitions {
|
||||
def.config.validate().map_err(Error::Domain)?;
|
||||
}
|
||||
|
||||
let is_single_window = definitions.len() == 1;
|
||||
|
||||
if is_single_window {
|
||||
let definition = definitions.into_iter().next().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Expected at least one window definition".to_string(),
|
||||
})
|
||||
})?;
|
||||
Self::new_single_window(compilation_result, definition)
|
||||
} else {
|
||||
Self::new_multi_window(compilation_result, &definitions)
|
||||
match definitions.len() {
|
||||
0 => {
|
||||
log::info!("Creating minimal shell without layer surfaces");
|
||||
Self::new_minimal(compilation_result)
|
||||
}
|
||||
1 => {
|
||||
let definition = definitions.into_iter().next().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Expected at least one window definition".to_string(),
|
||||
})
|
||||
})?;
|
||||
Self::new_single_window(compilation_result, definition)
|
||||
}
|
||||
_ => Self::new_multi_window(compilation_result, &definitions),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -545,6 +538,34 @@ impl Shell {
|
|||
Ok(shell)
|
||||
}
|
||||
|
||||
fn new_minimal(compilation_result: Rc<CompilationResult>) -> Result<Self> {
|
||||
let inner = layer_shika_adapters::WaylandShellSystem::new_minimal()?;
|
||||
let inner_rc: Rc<RefCell<dyn WaylandSystemOps>> = Rc::new(RefCell::new(inner));
|
||||
|
||||
inner_rc
|
||||
.borrow_mut()
|
||||
.set_compilation_result(Rc::clone(&compilation_result));
|
||||
|
||||
let (sender, receiver) = channel::channel();
|
||||
|
||||
let registry = SurfaceRegistry::new();
|
||||
|
||||
let shell = Self {
|
||||
inner: Rc::clone(&inner_rc),
|
||||
registry,
|
||||
compilation_result,
|
||||
command_sender: sender,
|
||||
output_connected_handlers: Rc::new(RefCell::new(Vec::new())),
|
||||
output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())),
|
||||
};
|
||||
|
||||
shell.setup_command_handler(receiver)?;
|
||||
|
||||
log::info!("Shell created (minimal mode - no layer surfaces)");
|
||||
|
||||
Ok(shell)
|
||||
}
|
||||
|
||||
fn setup_command_handler(&self, receiver: channel::Channel<ShellCommand>) -> Result<()> {
|
||||
let loop_handle = self.inner.borrow().event_loop_handle();
|
||||
let control = self.control();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue