diff --git a/crates/adapters/src/wayland/event_handling/app_dispatcher.rs b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs index 028b2f3..ac3e895 100644 --- a/crates/adapters/src/wayland/event_handling/app_dispatcher.rs +++ b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs @@ -169,16 +169,16 @@ impl Dispatch for AppState { if let Some(window) = state.get_output_by_surface_mut(&surface_id) { window.handle_pointer_enter(serial, &surface, surface_x, surface_y); - if let Some(key) = state.get_key_by_surface(&surface_id) { - state.set_active_output(Some(key)); + if let Some(handle) = state.get_handle_by_surface(&surface_id) { + state.set_active_output_handle(Some(handle)); } } else { - let key = state.get_key_by_popup(&surface_id); + let handle = state.get_handle_by_popup(&surface_id); if let Some(window) = state.find_output_by_popup_mut(&surface_id) { window.handle_pointer_enter(serial, &surface, surface_x, surface_y); - if let Some(key) = key { - state.set_active_output(Some(key)); + if let Some(handle) = handle { + state.set_active_output_handle(Some(handle)); } } } @@ -189,20 +189,20 @@ impl Dispatch for AppState { surface_y, .. } => { - if let Some(output_key) = state.active_output() { - if let Some(window) = state.get_output_by_key_mut(&output_key) { + if let Some(output_handle) = state.active_output_handle() { + if let Some(window) = state.get_output_by_handle_mut(output_handle) { window.handle_pointer_motion(surface_x, surface_y); } } } wl_pointer::Event::Leave { .. } => { - if let Some(output_key) = state.active_output() { - if let Some(window) = state.get_output_by_key_mut(&output_key) { + if let Some(output_handle) = state.active_output_handle() { + if let Some(window) = state.get_output_by_handle_mut(output_handle) { window.handle_pointer_leave(); } } - state.set_active_output(None); + state.set_active_output_handle(None); } wl_pointer::Event::Button { @@ -210,8 +210,8 @@ impl Dispatch for AppState { state: button_state, .. } => { - if let Some(output_key) = state.active_output() { - if let Some(window) = state.get_output_by_key_mut(&output_key) { + if let Some(output_handle) = state.active_output_handle() { + if let Some(window) = state.get_output_by_handle_mut(output_handle) { window.handle_pointer_button(serial, button_state); } } diff --git a/crates/adapters/src/wayland/outputs.rs b/crates/adapters/src/wayland/outputs.rs index 9f828a3..77084e5 100644 --- a/crates/adapters/src/wayland/outputs.rs +++ b/crates/adapters/src/wayland/outputs.rs @@ -1,42 +1,36 @@ use layer_shika_domain::value_objects::output_handle::OutputHandle; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; +use std::collections::HashMap; use wayland_client::backend::ObjectId; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct OutputKey { - handle: OutputHandle, - _object_id_hash: u64, +pub struct OutputMapping { + object_to_handle: HashMap, } -impl OutputKey { - pub fn new(object_id: &ObjectId) -> Self { - let mut hasher = DefaultHasher::new(); - object_id.hash(&mut hasher); - let object_id_hash = hasher.finish(); - +impl OutputMapping { + pub fn new() -> Self { Self { - handle: OutputHandle::new(), - _object_id_hash: object_id_hash, + object_to_handle: HashMap::new(), } } - pub const fn handle(&self) -> OutputHandle { - self.handle + pub fn insert(&mut self, object_id: ObjectId) -> OutputHandle { + let handle = OutputHandle::new(); + self.object_to_handle.insert(object_id, handle); + handle + } + + pub fn get(&self, object_id: &ObjectId) -> Option { + self.object_to_handle.get(object_id).copied() + } + + #[allow(dead_code)] + pub fn remove(&mut self, object_id: &ObjectId) -> Option { + self.object_to_handle.remove(object_id) } } -impl From for OutputKey { - fn from(id: ObjectId) -> Self { - Self::new(&id) - } -} - -impl From for OutputKey { - fn from(handle: OutputHandle) -> Self { - Self { - handle, - _object_id_hash: 0, - } +impl Default for OutputMapping { + fn default() -> Self { + Self::new() } } diff --git a/crates/adapters/src/wayland/surfaces/app_state.rs b/crates/adapters/src/wayland/surfaces/app_state.rs index fc6e18d..5809a74 100644 --- a/crates/adapters/src/wayland/surfaces/app_state.rs +++ b/crates/adapters/src/wayland/surfaces/app_state.rs @@ -1,7 +1,8 @@ use super::event_context::SharedPointerSerial; use super::surface_state::WindowState; use crate::wayland::managed_proxies::ManagedWlPointer; -use crate::wayland::outputs::OutputKey; +use crate::wayland::outputs::OutputMapping; +use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::value_objects::output_handle::OutputHandle; use layer_shika_domain::value_objects::output_info::OutputInfo; use std::collections::HashMap; @@ -12,27 +13,23 @@ use wayland_client::backend::ObjectId; pub type PerOutputWindow = WindowState; pub struct AppState { - outputs: HashMap, - output_info: HashMap, + output_registry: OutputRegistry, + output_mapping: OutputMapping, + windows: HashMap, surface_to_output: HashMap, - output_to_handle: HashMap, _pointer: ManagedWlPointer, shared_pointer_serial: Rc, - active_output: Option, - primary_output: Option, } impl AppState { pub fn new(pointer: ManagedWlPointer, shared_serial: Rc) -> Self { Self { - outputs: HashMap::new(), - output_info: HashMap::new(), + output_registry: OutputRegistry::new(), + output_mapping: OutputMapping::new(), + windows: HashMap::new(), surface_to_output: HashMap::new(), - output_to_handle: HashMap::new(), _pointer: pointer, shared_pointer_serial: shared_serial, - active_output: None, - primary_output: None, } } @@ -42,61 +39,48 @@ impl AppState { main_surface_id: ObjectId, window: PerOutputWindow, ) { - let key = OutputKey::new(&output_id); - let handle = key.handle(); - self.output_to_handle.insert(output_id, handle); + let handle = self.output_mapping.insert(output_id); self.surface_to_output.insert(main_surface_id, handle); - let is_primary = self.primary_output.is_none(); - if is_primary { - self.primary_output = Some(handle); - } + let is_primary = self.output_registry.is_empty(); let mut info = OutputInfo::new(handle); info.set_primary(is_primary); - self.output_info.insert(handle, info); - self.outputs.insert(handle, window); - } - - pub fn get_output_by_key(&self, key: &OutputKey) -> Option<&PerOutputWindow> { - self.outputs.get(&key.handle()) - } - - pub fn get_output_by_key_mut(&mut self, key: &OutputKey) -> Option<&mut PerOutputWindow> { - self.outputs.get_mut(&key.handle()) + self.output_registry.add(info); + self.windows.insert(handle, window); } pub fn get_output_by_handle(&self, handle: OutputHandle) -> Option<&PerOutputWindow> { - self.outputs.get(&handle) + self.windows.get(&handle) } pub fn get_output_by_handle_mut( &mut self, handle: OutputHandle, ) -> Option<&mut PerOutputWindow> { - self.outputs.get_mut(&handle) + self.windows.get_mut(&handle) } pub fn get_output_by_output_id(&self, output_id: &ObjectId) -> Option<&PerOutputWindow> { - self.output_to_handle + self.output_mapping .get(output_id) - .and_then(|handle| self.outputs.get(handle)) + .and_then(|handle| self.windows.get(&handle)) } pub fn get_output_by_output_id_mut( &mut self, output_id: &ObjectId, ) -> Option<&mut PerOutputWindow> { - self.output_to_handle + self.output_mapping .get(output_id) - .and_then(|handle| self.outputs.get_mut(handle)) + .and_then(|handle| self.windows.get_mut(&handle)) } pub fn get_output_by_surface(&self, surface_id: &ObjectId) -> Option<&PerOutputWindow> { self.surface_to_output .get(surface_id) - .and_then(|handle| self.outputs.get(handle)) + .and_then(|handle| self.windows.get(handle)) } pub fn get_output_by_surface_mut( @@ -105,78 +89,69 @@ impl AppState { ) -> Option<&mut PerOutputWindow> { self.surface_to_output .get(surface_id) - .and_then(|handle| self.outputs.get_mut(handle)) + .and_then(|handle| self.windows.get_mut(handle)) } pub fn get_output_by_layer_surface_mut( &mut self, layer_surface_id: &ObjectId, ) -> Option<&mut PerOutputWindow> { - self.outputs + self.windows .values_mut() .find(|window| window.layer_surface().as_ref().id() == *layer_surface_id) } - pub fn get_key_by_surface(&self, surface_id: &ObjectId) -> Option { - self.surface_to_output - .get(surface_id) - .map(|&handle| OutputKey::from(handle)) - } - - pub fn get_key_by_output_id(&self, output_id: &ObjectId) -> Option { - self.output_to_handle - .get(output_id) - .map(|&handle| OutputKey::from(handle)) - } - pub fn get_handle_by_surface(&self, surface_id: &ObjectId) -> Option { self.surface_to_output.get(surface_id).copied() } pub fn get_handle_by_output_id(&self, output_id: &ObjectId) -> Option { - self.output_to_handle.get(output_id).copied() + self.output_mapping.get(output_id) } - pub fn register_popup_surface(&mut self, popup_surface_id: ObjectId, output_key: OutputKey) { + pub fn register_popup_surface( + &mut self, + popup_surface_id: ObjectId, + output_handle: OutputHandle, + ) { self.surface_to_output - .insert(popup_surface_id, output_key.handle()); - } - - pub fn set_active_output(&mut self, key: Option) { - self.active_output = key.map(|k| k.handle()); + .insert(popup_surface_id, output_handle); } pub fn set_active_output_handle(&mut self, handle: Option) { - self.active_output = handle; - } - - pub fn active_output(&self) -> Option { - self.active_output.map(OutputKey::from) + self.output_registry.set_active(handle); } pub fn active_output_handle(&self) -> Option { - self.active_output + self.output_registry.active_handle() } pub fn primary_output(&self) -> Option<&PerOutputWindow> { - self.primary_output - .and_then(|handle| self.outputs.get(&handle)) + self.output_registry + .primary_handle() + .and_then(|handle| self.windows.get(&handle)) } pub fn primary_output_handle(&self) -> Option { - self.primary_output + self.output_registry.primary_handle() + } + + pub fn active_output(&self) -> Option<&PerOutputWindow> { + self.output_registry + .active_handle() + .and_then(|handle| self.windows.get(&handle)) } pub fn all_outputs(&self) -> impl Iterator { - self.outputs.values() + self.windows.values() } pub fn all_outputs_mut(&mut self) -> impl Iterator { - self.outputs.values_mut() + self.windows.values_mut() } pub fn outputs_with_handles(&self) -> impl Iterator { - self.outputs + self.windows .iter() .map(|(&handle, window)| (handle, window)) } @@ -186,7 +161,7 @@ impl AppState { } pub fn find_output_by_popup(&self, popup_surface_id: &ObjectId) -> Option<&PerOutputWindow> { - self.outputs.values().find(|window| { + self.windows.values().find(|window| { window .popup_manager() .as_ref() @@ -199,7 +174,7 @@ impl AppState { &mut self, popup_surface_id: &ObjectId, ) -> Option<&mut PerOutputWindow> { - self.outputs.values_mut().find(|window| { + self.windows.values_mut().find(|window| { window .popup_manager() .as_ref() @@ -208,18 +183,8 @@ impl AppState { }) } - pub fn get_key_by_popup(&self, popup_surface_id: &ObjectId) -> Option { - self.outputs.iter().find_map(|(&handle, window)| { - window - .popup_manager() - .as_ref() - .and_then(|pm| pm.find_by_surface(popup_surface_id)) - .map(|_| OutputKey::from(handle)) - }) - } - pub fn get_handle_by_popup(&self, popup_surface_id: &ObjectId) -> Option { - self.outputs.iter().find_map(|(&handle, window)| { + self.windows.iter().find_map(|(&handle, window)| { window .popup_manager() .as_ref() @@ -229,20 +194,24 @@ impl AppState { } pub fn get_output_info(&self, handle: OutputHandle) -> Option<&OutputInfo> { - self.output_info.get(&handle) + self.output_registry.get(handle) } pub fn get_output_info_mut(&mut self, handle: OutputHandle) -> Option<&mut OutputInfo> { - self.output_info.get_mut(&handle) + self.output_registry.get_mut(handle) } pub fn all_output_info(&self) -> impl Iterator { - self.output_info.values() + self.output_registry.all_info() } pub fn outputs_with_info(&self) -> impl Iterator { - self.output_info - .iter() - .filter_map(|(handle, info)| self.outputs.get(handle).map(|window| (info, window))) + self.output_registry + .all() + .filter_map(|(handle, info)| self.windows.get(&handle).map(|window| (info, window))) + } + + pub const fn output_registry(&self) -> &OutputRegistry { + &self.output_registry } } diff --git a/crates/composition/src/lib.rs b/crates/composition/src/lib.rs index a3653cc..6e17718 100644 --- a/crates/composition/src/lib.rs +++ b/crates/composition/src/lib.rs @@ -11,6 +11,7 @@ use std::result::Result as StdResult; pub use builder::LayerShika; pub use layer_shika_adapters::PopupWindow; pub use layer_shika_adapters::platform::{slint, slint_interpreter}; +pub use layer_shika_domain::entities::output_registry::OutputRegistry; pub use layer_shika_domain::value_objects::anchor::AnchorEdges; pub use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity; pub use layer_shika_domain::value_objects::layer::Layer; diff --git a/crates/composition/src/system.rs b/crates/composition/src/system.rs index 480f85c..55cde5a 100644 --- a/crates/composition/src/system.rs +++ b/crates/composition/src/system.rs @@ -13,6 +13,7 @@ use layer_shika_adapters::{ AppState, PopupManager, WaylandWindowConfig, WindowState, WindowingSystemFacade, }; use layer_shika_domain::config::WindowConfig; +use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::errors::DomainError; use layer_shika_domain::value_objects::output_handle::OutputHandle; use layer_shika_domain::value_objects::output_info::OutputInfo; @@ -141,6 +142,10 @@ impl RuntimeState<'_> { .map(WindowState::component_instance) } + pub const fn output_registry(&self) -> &OutputRegistry { + self.app_state.output_registry() + } + #[must_use] pub fn primary_output_handle(&self) -> Option { self.app_state.primary_output_handle() @@ -180,7 +185,6 @@ impl RuntimeState<'_> { fn active_or_primary_output(&self) -> Option<&WindowState> { self.app_state .active_output() - .and_then(|key| self.app_state.get_output_by_key(&key)) .or_else(|| self.app_state.primary_output()) } @@ -579,4 +583,10 @@ impl WindowingSystem { let system = facade.inner_ref(); system.app_state().all_output_info().cloned().collect() } + + pub fn output_registry(&self) -> OutputRegistry { + let facade = self.inner.borrow(); + let system = facade.inner_ref(); + system.app_state().output_registry().clone() + } } diff --git a/crates/domain/src/entities/mod.rs b/crates/domain/src/entities/mod.rs index 0466ae4..36eb4bf 100644 --- a/crates/domain/src/entities/mod.rs +++ b/crates/domain/src/entities/mod.rs @@ -1,3 +1,4 @@ pub mod component; +pub mod output_registry; pub mod surface; pub mod window; diff --git a/crates/domain/src/entities/output_registry.rs b/crates/domain/src/entities/output_registry.rs new file mode 100644 index 0000000..43eb9a2 --- /dev/null +++ b/crates/domain/src/entities/output_registry.rs @@ -0,0 +1,156 @@ +use crate::value_objects::output_handle::OutputHandle; +use crate::value_objects::output_info::OutputInfo; +use crate::value_objects::output_policy::OutputPolicy; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct OutputRegistry { + outputs: HashMap, + primary_output: Option, + active_output: Option, +} + +impl OutputRegistry { + pub fn new() -> Self { + Self { + outputs: HashMap::new(), + primary_output: None, + active_output: None, + } + } + + pub fn add(&mut self, info: OutputInfo) -> OutputHandle { + let handle = info.handle(); + let is_first = self.outputs.is_empty(); + + self.outputs.insert(handle, info); + + if is_first { + self.primary_output = Some(handle); + } + + handle + } + + pub fn remove(&mut self, handle: OutputHandle) -> Option { + let info = self.outputs.remove(&handle); + + if self.primary_output == Some(handle) { + self.primary_output = self.outputs.keys().next().copied(); + } + + if self.active_output == Some(handle) { + self.active_output = None; + } + + info + } + + pub fn get(&self, handle: OutputHandle) -> Option<&OutputInfo> { + self.outputs.get(&handle) + } + + pub fn get_mut(&mut self, handle: OutputHandle) -> Option<&mut OutputInfo> { + self.outputs.get_mut(&handle) + } + + pub fn find_by_name(&self, name: &str) -> Option<(OutputHandle, &OutputInfo)> { + self.outputs + .iter() + .find(|(_, info)| info.name() == Some(name)) + .map(|(handle, info)| (*handle, info)) + } + + pub fn find_by_model(&self, model: &str) -> Option<(OutputHandle, &OutputInfo)> { + self.outputs + .iter() + .find(|(_, info)| info.geometry().and_then(|g| g.model.as_deref()) == Some(model)) + .map(|(handle, info)| (*handle, info)) + } + + pub fn all(&self) -> impl Iterator { + self.outputs.iter().map(|(handle, info)| (*handle, info)) + } + + pub fn all_info(&self) -> impl Iterator { + self.outputs.values() + } + + pub fn primary(&self) -> Option<(OutputHandle, &OutputInfo)> { + self.primary_output + .and_then(|handle| self.outputs.get(&handle).map(|info| (handle, info))) + } + + pub fn primary_handle(&self) -> Option { + self.primary_output + } + + pub fn active(&self) -> Option<(OutputHandle, &OutputInfo)> { + self.active_output + .and_then(|handle| self.outputs.get(&handle).map(|info| (handle, info))) + } + + pub fn active_handle(&self) -> Option { + self.active_output + } + + pub fn active_or_primary(&self) -> Option<(OutputHandle, &OutputInfo)> { + self.active().or_else(|| self.primary()) + } + + pub fn active_or_primary_handle(&self) -> Option { + self.active_output.or(self.primary_output) + } + + pub fn set_active(&mut self, handle: Option) { + if let Some(h) = handle { + if self.outputs.contains_key(&h) { + self.active_output = Some(h); + } + } else { + self.active_output = None; + } + } + + pub fn set_primary(&mut self, handle: OutputHandle) -> bool { + if !self.outputs.contains_key(&handle) { + return false; + } + + if let Some(old_primary) = self.primary_output { + if let Some(old_info) = self.outputs.get_mut(&old_primary) { + old_info.set_primary(false); + } + } + + self.primary_output = Some(handle); + + if let Some(new_info) = self.outputs.get_mut(&handle) { + new_info.set_primary(true); + } + + true + } + + pub fn select_by_policy(&self, policy: &OutputPolicy) -> Vec<(OutputHandle, &OutputInfo)> { + self.outputs + .iter() + .filter(|(_, info)| policy.should_render(info)) + .map(|(handle, info)| (*handle, info)) + .collect() + } + + pub fn count(&self) -> usize { + self.outputs.len() + } + + pub fn is_empty(&self) -> bool { + self.outputs.is_empty() + } +} + +impl Default for OutputRegistry { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index c957d75..c3dab49 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -1,6 +1,6 @@ pub mod config; pub mod dimensions; -pub(crate) mod entities; +pub mod entities; pub mod errors; pub mod ports; pub mod prelude; diff --git a/crates/domain/src/prelude.rs b/crates/domain/src/prelude.rs index 0066db9..384fdb7 100644 --- a/crates/domain/src/prelude.rs +++ b/crates/domain/src/prelude.rs @@ -5,6 +5,7 @@ pub use crate::dimensions::{ LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, ScaleFactor, }; pub use crate::entities::component::UiComponentHandle; +pub use crate::entities::output_registry::OutputRegistry; pub use crate::entities::surface::SurfaceHandle; pub use crate::entities::window::WindowHandle; pub use crate::errors::{DomainError, Result}; diff --git a/src/lib.rs b/src/lib.rs index 3d64908..9a3210f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ pub mod prelude; pub use layer_shika_composition::{ AnchorEdges, Error, EventLoopHandle, KeyboardInteractivity, Layer, LayerShika, OutputGeometry, - OutputHandle, OutputInfo, OutputPolicy, PopupAt, PopupHandle, PopupPositioningMode, + OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupAt, PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, RuntimeState, SlintCallbackContract, SlintCallbackNames, WindowingSystem, }; diff --git a/src/prelude.rs b/src/prelude.rs index da5ac50..3eab030 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -17,7 +17,7 @@ pub use crate::{ // Domain value objects pub use crate::{ AnchorEdges, KeyboardInteractivity, Layer, OutputGeometry, OutputHandle, OutputInfo, - OutputPolicy, PopupAt, PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, + OutputPolicy, OutputRegistry, PopupAt, PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, }; // Event loop types