From 6353a786af05bd09575a93abb78c17efaaa84f0b Mon Sep 17 00:00:00 2001 From: drendog Date: Tue, 28 Oct 2025 06:18:37 +0100 Subject: [PATCH] refactor: migrate to reworked architecture --- Cargo.lock | 17 ++++ Cargo.toml | 75 ++++++--------- adapters/Cargo.toml | 29 ++++++ {src => adapters/src}/errors.rs | 4 + adapters/src/event_loop/calloop_adapter.rs | 79 ++++++++++++++++ adapters/src/event_loop/mod.rs | 1 + adapters/src/lib.rs | 18 ++++ .../src/rendering/egl/context.rs | 7 ++ adapters/src/rendering/egl/mod.rs | 1 + .../src/rendering/femtovg/main_window.rs | 1 + adapters/src/rendering/femtovg/mod.rs | 2 + .../src/rendering/femtovg}/popup_window.rs | 3 +- adapters/src/rendering/mod.rs | 3 + .../src/rendering/slint_integration/mod.rs | 1 + .../rendering/slint_integration/platform.rs | 3 +- adapters/src/wayland/config.rs | 76 +++++++++++++++ adapters/src/wayland/connection.rs | 10 ++ .../event_handling/event_dispatcher.rs | 8 +- .../wayland/event_handling/event_macros.rs | 0 adapters/src/wayland/event_handling/mod.rs | 2 + .../src/wayland/globals/context.rs | 7 +- adapters/src/wayland/globals/mod.rs | 1 + .../src/wayland/managed_proxies.rs | 5 + adapters/src/wayland/mod.rs | 7 ++ .../src/wayland/shell_adapter.rs | 31 ++++--- adapters/src/wayland/surfaces/dimensions.rs | 17 ++++ .../src/wayland/surfaces/layer_surface.rs | 7 +- adapters/src/wayland/surfaces/mod.rs | 6 ++ .../src/wayland/surfaces}/popup_manager.rs | 10 +- .../src/wayland/surfaces/popup_surface.rs | 2 +- .../src/wayland/surfaces/surface_builder.rs | 10 +- .../src/wayland/surfaces/surface_state.rs | 42 +++++---- {tests => adapters/tests}/.gitkeep | 0 composition/Cargo.toml | 18 ++++ composition/src/builder.rs | 79 ++++++++++++++++ composition/src/lib.rs | 23 +++++ composition/src/system.rs | 83 +++++++++++++++++ domain/Cargo.toml | 12 +++ domain/src/config.rs | 39 ++++++++ domain/src/entities/component.rs | 14 +++ domain/src/entities/mod.rs | 3 + domain/src/entities/surface.rs | 14 +++ domain/src/entities/window.rs | 14 +++ domain/src/errors.rs | 19 ++++ domain/src/events/mod.rs | 1 + domain/src/events/window_events.rs | 15 +++ domain/src/lib.rs | 6 ++ .../src}/surface_dimensions.rs | 11 +-- domain/src/value_objects/anchor.rs | 92 ++++++++++++++++++ domain/src/value_objects/dimensions.rs | 20 ++++ domain/src/value_objects/layer.rs | 13 +++ domain/src/value_objects/margins.rs | 7 ++ domain/src/value_objects/mod.rs | 4 + src/lib.rs | 8 -- src/reexports.rs | 10 -- src/rendering/mod.rs | 4 - src/windowing/builder.rs | 93 ------------------- src/windowing/config.rs | 51 ---------- src/windowing/mod.rs | 13 --- 59 files changed, 861 insertions(+), 290 deletions(-) create mode 100644 adapters/Cargo.toml rename {src => adapters/src}/errors.rs (91%) create mode 100644 adapters/src/event_loop/calloop_adapter.rs create mode 100644 adapters/src/event_loop/mod.rs create mode 100644 adapters/src/lib.rs rename src/rendering/egl_context.rs => adapters/src/rendering/egl/context.rs (98%) create mode 100644 adapters/src/rendering/egl/mod.rs rename src/rendering/femtovg_window.rs => adapters/src/rendering/femtovg/main_window.rs (99%) create mode 100644 adapters/src/rendering/femtovg/mod.rs rename {src/rendering => adapters/src/rendering/femtovg}/popup_window.rs (98%) create mode 100644 adapters/src/rendering/mod.rs create mode 100644 adapters/src/rendering/slint_integration/mod.rs rename src/rendering/slint_platform.rs => adapters/src/rendering/slint_integration/platform.rs (94%) create mode 100644 adapters/src/wayland/config.rs create mode 100644 adapters/src/wayland/connection.rs rename src/windowing/state/dispatches.rs => adapters/src/wayland/event_handling/event_dispatcher.rs (98%) rename src/windowing/macros.rs => adapters/src/wayland/event_handling/event_macros.rs (100%) create mode 100644 adapters/src/wayland/event_handling/mod.rs rename src/windowing/globals.rs => adapters/src/wayland/globals/context.rs (95%) create mode 100644 adapters/src/wayland/globals/mod.rs rename src/windowing/proxies.rs => adapters/src/wayland/managed_proxies.rs (98%) create mode 100644 adapters/src/wayland/mod.rs rename src/windowing/system.rs => adapters/src/wayland/shell_adapter.rs (91%) create mode 100644 adapters/src/wayland/surfaces/dimensions.rs rename src/windowing/surface.rs => adapters/src/wayland/surfaces/layer_surface.rs (93%) create mode 100644 adapters/src/wayland/surfaces/mod.rs rename {src/windowing => adapters/src/wayland/surfaces}/popup_manager.rs (95%) rename src/windowing/popup.rs => adapters/src/wayland/surfaces/popup_surface.rs (99%) rename src/windowing/state/builder.rs => adapters/src/wayland/surfaces/surface_builder.rs (93%) rename src/windowing/state/mod.rs => adapters/src/wayland/surfaces/surface_state.rs (94%) rename {tests => adapters/tests}/.gitkeep (100%) create mode 100644 composition/Cargo.toml create mode 100644 composition/src/builder.rs create mode 100644 composition/src/lib.rs create mode 100644 composition/src/system.rs create mode 100644 domain/Cargo.toml create mode 100644 domain/src/config.rs create mode 100644 domain/src/entities/component.rs create mode 100644 domain/src/entities/mod.rs create mode 100644 domain/src/entities/surface.rs create mode 100644 domain/src/entities/window.rs create mode 100644 domain/src/errors.rs create mode 100644 domain/src/events/mod.rs create mode 100644 domain/src/events/window_events.rs create mode 100644 domain/src/lib.rs rename {src/windowing => domain/src}/surface_dimensions.rs (74%) create mode 100644 domain/src/value_objects/anchor.rs create mode 100644 domain/src/value_objects/dimensions.rs create mode 100644 domain/src/value_objects/layer.rs create mode 100644 domain/src/value_objects/margins.rs create mode 100644 domain/src/value_objects/mod.rs delete mode 100644 src/lib.rs delete mode 100644 src/reexports.rs delete mode 100644 src/rendering/mod.rs delete mode 100644 src/windowing/builder.rs delete mode 100644 src/windowing/config.rs delete mode 100644 src/windowing/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 9dffe4d..bcc6957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1854,8 +1854,18 @@ dependencies = [ [[package]] name = "layer-shika" version = "0.1.0" +dependencies = [ + "layer-shika-adapters", + "layer-shika-domain", + "thiserror 2.0.17", +] + +[[package]] +name = "layer-shika-adapters" +version = "0.1.0" dependencies = [ "glutin", + "layer-shika-domain", "log", "raw-window-handle", "slint", @@ -1866,6 +1876,13 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "layer-shika-domain" +version = "0.1.0" +dependencies = [ + "thiserror 2.0.17", +] + [[package]] name = "lazy-bytes-cast" version = "5.0.1" diff --git a/Cargo.toml b/Cargo.toml index 3e245c1..a93d0a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,48 +1,31 @@ -[package] -name = "layer-shika" -version = "0.1.0" -edition = "2021" -description = "A layer shell library crate with Slint UI" -license = "AGPL-3.0-or-later" -repository = "https://codeberg.org/waydeer/layer-shika" -readme = "README.md" -keywords = ["layer-shell", "wayland", "slint", "femtovg", "smithay"] -categories = ["gui"] +[workspace] +resolver = "2" +members = ["domain", "adapters", "composition"] -[lints.clippy] -all = { level = "warn", priority = -1 } -cargo = { level = "warn", priority = -1 } -nursery = { level = "warn", priority = -1 } -pedantic = { level = "warn", priority = -1 } -clone_on_ref_ptr = "warn" -multiple-crate-versions = "allow" -module_name_repetitions = "allow" -unwrap_used = "warn" +[workspace.lints.clippy] +all = { level = "deny", priority = -1 } +pedantic = { level = "deny", priority = -1 } +disallowed_types = "deny" +disallowed_methods = "deny" +pub_use = "deny" +unwrap_used = "deny" +expect_used = "deny" +print_stdout = "deny" +print_stderr = "deny" +panic = "deny" +indexing_slicing = "deny" +exit = "deny" +redundant_clone = "deny" +clone_on_ref_ptr = "deny" +vec_box = "deny" +large_enum_variant = "deny" +await_holding_lock = "deny" +future_not_send = "deny" +cognitive_complexity = "deny" +needless_collect = "deny" +let_underscore_must_use = "deny" absolute_paths = "deny" - -[dependencies] -glutin = { version = "0.32.3", default-features = false, features = [ - "wayland", -] } -log = "0.4.28" -raw-window-handle = "0.6.2" -slint = { version = "1.14.1", default-features = false, features = [ - "compat-1-2", - "renderer-femtovg", - "backend-winit-wayland", -] } -# slint = { path = "../slint/api/rs/slint", default-features = false, features = [ -# "compat-1-2", -# "renderer-femtovg", -# "backend-winit-wayland", -# ] } -slint-interpreter = { version = "1.14.1", default-features = false, features = [ - "compat-1-2", -] } -# slint-interpreter = { path = "../slint/internal/interpreter", default-features = false, features = [ -# "compat-1-2", -# ] } -smithay-client-toolkit = "0.20.0" -thiserror = "2.0.17" -wayland-client = "0.31.11" -wayland-protocols = { version = "0.32.9", features = ["client", "staging"] } +missing_errors_doc = "allow" +missing_panics_doc = "allow" +must_use_candidate = "allow" +uninlined_format_args = "allow" diff --git a/adapters/Cargo.toml b/adapters/Cargo.toml new file mode 100644 index 0000000..6cf042f --- /dev/null +++ b/adapters/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "layer-shika-adapters" +version = "0.1.0" +edition = "2021" +description = "Adapters layer for layer-shika" +license = "AGPL-3.0-or-later" + +[lints] +workspace = true + +[dependencies] +glutin = { version = "0.32.3", default-features = false, features = [ + "wayland", +] } +layer-shika-domain = { version = "0.1.0", path = "../domain" } +log = "0.4.28" +raw-window-handle = "0.6.2" +slint = { version = "1.14.1", default-features = false, features = [ + "compat-1-2", + "renderer-femtovg", + "backend-winit-wayland", +] } +slint-interpreter = { version = "1.14.1", default-features = false, features = [ + "compat-1-2", +] } +smithay-client-toolkit = "0.20.0" +thiserror = "2.0.17" +wayland-client = "0.31.11" +wayland-protocols = { version = "0.32.9", features = ["client", "staging"] } diff --git a/src/errors.rs b/adapters/src/errors.rs similarity index 91% rename from src/errors.rs rename to adapters/src/errors.rs index 3e2c1a7..581ab07 100644 --- a/src/errors.rs +++ b/adapters/src/errors.rs @@ -1,3 +1,4 @@ +use layer_shika_domain::errors::DomainError; use std::result::Result as StdResult; use thiserror::Error; use wayland_client::backend::WaylandError; @@ -6,6 +7,9 @@ pub type Result = StdResult; #[derive(Error, Debug)] pub enum LayerShikaError { + #[error("Domain error: {0}")] + Domain(#[from] DomainError), + #[error("Failed to connect to Wayland: {0}")] WaylandConnection(#[from] wayland_client::ConnectError), diff --git a/adapters/src/event_loop/calloop_adapter.rs b/adapters/src/event_loop/calloop_adapter.rs new file mode 100644 index 0000000..63dd17a --- /dev/null +++ b/adapters/src/event_loop/calloop_adapter.rs @@ -0,0 +1,79 @@ +use crate::wayland::{surfaces::surface_state::WindowState, shell_adapter::WaylandWindowingSystem}; +use crate::{ + errors::Result, + platform::calloop::{EventSource, InsertError, RegistrationToken}, +}; +use slint_interpreter::ComponentInstance; +use std::result::Result as StdResult; + +pub struct SystemAdapter { + inner: WaylandWindowingSystem, +} + +impl SystemAdapter { + #[must_use] + pub fn new(inner: WaylandWindowingSystem) -> Self { + Self { inner } + } + + #[must_use] + pub fn event_loop_handle(&self) -> EventLoopAdapter { + EventLoopAdapter { + inner_system: std::ptr::addr_of!(self.inner), + } + } + + pub fn run(&mut self) -> Result<()> { + self.inner.run() + } + + pub const fn component_instance(&self) -> &ComponentInstance { + self.inner.component_instance() + } +} + +pub struct EventLoopAdapter { + inner_system: *const WaylandWindowingSystem, +} + +unsafe impl Send for EventLoopAdapter {} +unsafe impl Sync for EventLoopAdapter {} + +impl EventLoopAdapter { + pub fn insert_source_with_adapter( + &self, + source: S, + mut callback: F, + ) -> StdResult> + where + S: EventSource + 'static, + F: FnMut(S::Event, &mut S::Metadata, RuntimeStateAdapter) -> R + 'static, + { + let inner_system = unsafe { &*self.inner_system }; + let loop_handle = inner_system.event_loop_handle(); + + loop_handle.insert_source(source, move |event, metadata, window_state| { + let runtime_state = RuntimeStateAdapter { + window_state: std::ptr::addr_of_mut!(*window_state), + }; + callback(event, metadata, runtime_state) + }) + } +} + +pub struct RuntimeStateAdapter { + window_state: *mut WindowState, +} + +impl RuntimeStateAdapter { + #[must_use] + pub fn component_instance(&self) -> &ComponentInstance { + let window_state = unsafe { &*self.window_state }; + window_state.component_instance() + } + + pub fn render_frame_if_dirty(&self) -> Result<()> { + let window_state = unsafe { &*self.window_state }; + window_state.render_frame_if_dirty() + } +} diff --git a/adapters/src/event_loop/mod.rs b/adapters/src/event_loop/mod.rs new file mode 100644 index 0000000..bdb5b07 --- /dev/null +++ b/adapters/src/event_loop/mod.rs @@ -0,0 +1 @@ +pub mod calloop_adapter; diff --git a/adapters/src/lib.rs b/adapters/src/lib.rs new file mode 100644 index 0000000..0ddda8f --- /dev/null +++ b/adapters/src/lib.rs @@ -0,0 +1,18 @@ +#![allow(clippy::pub_use)] + +pub mod errors; +pub mod event_loop; +pub mod rendering; +pub mod wayland; + +pub mod platform { + pub use slint; + pub use slint_interpreter; + + pub mod calloop { + pub use smithay_client_toolkit::reexports::calloop::channel; + pub use smithay_client_toolkit::reexports::calloop::{ + EventSource, InsertError, PostAction, RegistrationToken, + }; + } +} diff --git a/src/rendering/egl_context.rs b/adapters/src/rendering/egl/context.rs similarity index 98% rename from src/rendering/egl_context.rs rename to adapters/src/rendering/egl/context.rs index d0d755a..d6eaf1f 100644 --- a/src/rendering/egl_context.rs +++ b/adapters/src/rendering/egl/context.rs @@ -40,31 +40,37 @@ pub struct EGLContextBuilder { } impl EGLContextBuilder { + #[must_use] pub fn new() -> Self { Self::default() } + #[must_use] pub fn with_display_id(mut self, display_id: ObjectId) -> Self { self.display_id = Some(display_id); self } + #[must_use] pub fn with_surface_id(mut self, surface_id: ObjectId) -> Self { self.surface_id = Some(surface_id); self } + #[must_use] pub const fn with_size(mut self, size: PhysicalSize) -> Self { self.size = Some(size); self } + #[must_use] #[allow(dead_code)] pub const fn with_config_template(mut self, config_template: ConfigTemplateBuilder) -> Self { self.config_template = Some(config_template); self } + #[must_use] #[allow(dead_code)] pub const fn with_context_attributes( mut self, @@ -110,6 +116,7 @@ impl EGLContextBuilder { } impl EGLContext { + #[must_use] pub fn builder() -> EGLContextBuilder { EGLContextBuilder::new() } diff --git a/adapters/src/rendering/egl/mod.rs b/adapters/src/rendering/egl/mod.rs new file mode 100644 index 0000000..9efb2ab --- /dev/null +++ b/adapters/src/rendering/egl/mod.rs @@ -0,0 +1 @@ +pub mod context; diff --git a/src/rendering/femtovg_window.rs b/adapters/src/rendering/femtovg/main_window.rs similarity index 99% rename from src/rendering/femtovg_window.rs rename to adapters/src/rendering/femtovg/main_window.rs index d39329a..e7d8876 100644 --- a/src/rendering/femtovg_window.rs +++ b/adapters/src/rendering/femtovg/main_window.rs @@ -22,6 +22,7 @@ pub struct FemtoVGWindow { } impl FemtoVGWindow { + #[must_use] pub fn new(renderer: FemtoVGRenderer) -> Rc { Rc::new_cyclic(|weak_self| { let window = Window::new(Weak::clone(weak_self) as Weak); diff --git a/adapters/src/rendering/femtovg/mod.rs b/adapters/src/rendering/femtovg/mod.rs new file mode 100644 index 0000000..50d701a --- /dev/null +++ b/adapters/src/rendering/femtovg/mod.rs @@ -0,0 +1,2 @@ +pub mod main_window; +pub mod popup_window; diff --git a/src/rendering/popup_window.rs b/adapters/src/rendering/femtovg/popup_window.rs similarity index 98% rename from src/rendering/popup_window.rs rename to adapters/src/rendering/femtovg/popup_window.rs index 0d376b1..cc9b13a 100644 --- a/src/rendering/popup_window.rs +++ b/adapters/src/rendering/femtovg/popup_window.rs @@ -8,7 +8,7 @@ use slint::{ use std::cell::Cell; use std::rc::{Rc, Weak}; -use super::femtovg_window::RenderState; +use super::main_window::RenderState; #[allow(dead_code)] pub struct PopupWindow { @@ -21,6 +21,7 @@ pub struct PopupWindow { #[allow(dead_code)] impl PopupWindow { + #[must_use] pub fn new(renderer: FemtoVGRenderer) -> Rc { Rc::new_cyclic(|weak_self| { let window = Window::new(Weak::clone(weak_self) as Weak); diff --git a/adapters/src/rendering/mod.rs b/adapters/src/rendering/mod.rs new file mode 100644 index 0000000..d6096e1 --- /dev/null +++ b/adapters/src/rendering/mod.rs @@ -0,0 +1,3 @@ +pub mod egl; +pub mod femtovg; +pub mod slint_integration; diff --git a/adapters/src/rendering/slint_integration/mod.rs b/adapters/src/rendering/slint_integration/mod.rs new file mode 100644 index 0000000..3be4447 --- /dev/null +++ b/adapters/src/rendering/slint_integration/mod.rs @@ -0,0 +1 @@ +pub mod platform; diff --git a/src/rendering/slint_platform.rs b/adapters/src/rendering/slint_integration/platform.rs similarity index 94% rename from src/rendering/slint_platform.rs rename to adapters/src/rendering/slint_integration/platform.rs index 572981b..d688ffb 100644 --- a/src/rendering/slint_platform.rs +++ b/adapters/src/rendering/slint_integration/platform.rs @@ -5,7 +5,7 @@ use slint::{ use std::cell::{Cell, RefCell}; use std::rc::{Rc, Weak}; -use super::femtovg_window::FemtoVGWindow; +use crate::rendering::femtovg::main_window::FemtoVGWindow; type PopupCreator = dyn Fn() -> Result, PlatformError>; @@ -16,6 +16,7 @@ pub struct CustomSlintPlatform { } impl CustomSlintPlatform { + #[must_use] pub fn new(window: &Rc) -> Self { Self { main_window: Rc::downgrade(window), diff --git a/adapters/src/wayland/config.rs b/adapters/src/wayland/config.rs new file mode 100644 index 0000000..9dea023 --- /dev/null +++ b/adapters/src/wayland/config.rs @@ -0,0 +1,76 @@ +use layer_shika_domain::config::{AnchorEdges, Layer, Margins, WindowConfig as DomainWindowConfig}; +use slint_interpreter::ComponentDefinition; +use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ + zwlr_layer_shell_v1::{self}, + zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity}, +}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct LayerSurfaceParams { + pub anchor: Anchor, + pub margin: Margins, + pub exclusive_zone: i32, + pub keyboard_interactivity: KeyboardInteractivity, + pub height: u32, +} + +#[derive(Clone)] +pub struct WaylandWindowConfig { + pub height: u32, + pub layer: zwlr_layer_shell_v1::Layer, + pub margin: Margins, + pub anchor: Anchor, + pub keyboard_interactivity: KeyboardInteractivity, + pub exclusive_zone: i32, + pub scale_factor: f32, + pub namespace: String, + pub component_definition: ComponentDefinition, +} + +impl WaylandWindowConfig { + #[must_use] + pub fn from_domain_config( + component_definition: ComponentDefinition, + domain_config: DomainWindowConfig, + ) -> Self { + Self { + height: domain_config.height, + layer: convert_layer(domain_config.layer), + margin: domain_config.margin, + anchor: convert_anchor(domain_config.anchor), + keyboard_interactivity: KeyboardInteractivity::OnDemand, + exclusive_zone: domain_config.exclusive_zone, + scale_factor: domain_config.scale_factor, + namespace: domain_config.namespace, + component_definition, + } + } +} + +const fn convert_layer(layer: Layer) -> zwlr_layer_shell_v1::Layer { + match layer { + Layer::Background => zwlr_layer_shell_v1::Layer::Background, + Layer::Bottom => zwlr_layer_shell_v1::Layer::Bottom, + Layer::Top => zwlr_layer_shell_v1::Layer::Top, + Layer::Overlay => zwlr_layer_shell_v1::Layer::Overlay, + } +} + +const fn convert_anchor(anchor: AnchorEdges) -> Anchor { + let mut result = Anchor::empty(); + + if anchor.has_top() { + result = result.union(Anchor::Top); + } + if anchor.has_bottom() { + result = result.union(Anchor::Bottom); + } + if anchor.has_left() { + result = result.union(Anchor::Left); + } + if anchor.has_right() { + result = result.union(Anchor::Right); + } + + result +} diff --git a/adapters/src/wayland/connection.rs b/adapters/src/wayland/connection.rs new file mode 100644 index 0000000..73c0b0d --- /dev/null +++ b/adapters/src/wayland/connection.rs @@ -0,0 +1,10 @@ +use crate::errors::{LayerShikaError, Result}; +use std::rc::Rc; +use wayland_client::{Connection, EventQueue}; + +pub fn initialize_wayland() -> Result<(Rc, EventQueue)> { + let connection = + Rc::new(Connection::connect_to_env().map_err(LayerShikaError::WaylandConnection)?); + let event_queue = connection.new_event_queue(); + Ok((connection, event_queue)) +} diff --git a/src/windowing/state/dispatches.rs b/adapters/src/wayland/event_handling/event_dispatcher.rs similarity index 98% rename from src/windowing/state/dispatches.rs rename to adapters/src/wayland/event_handling/event_dispatcher.rs index f91c5a6..d9af65b 100644 --- a/src/windowing/state/dispatches.rs +++ b/adapters/src/wayland/event_handling/event_dispatcher.rs @@ -1,4 +1,4 @@ -use super::WindowState; +use crate::wayland::surfaces::surface_state::WindowState; use crate::impl_empty_dispatch; use log::info; use slint::{ @@ -271,14 +271,14 @@ impl Dispatch for WindowState { info!("XdgPopup dismissed by compositor"); let popup_id = xdg_popup.id(); let popup_index = state - .popup_manager + .popup_manager() .as_ref() .and_then(|pm| pm.find_popup_index_by_xdg_popup_id(&popup_id)); if let Some(index) = popup_index { info!("Destroying popup at index {index}"); state.clear_active_window_if_popup(index); - if let Some(popup_manager) = &state.popup_manager { + if let Some(popup_manager) = &state.popup_manager() { popup_manager.destroy_popup(index); } } @@ -304,7 +304,7 @@ impl Dispatch for WindowState { info!("XdgSurface Configure received, sending ack with serial {serial}"); xdg_surface.ack_configure(serial); - if let Some(popup_manager) = &state.popup_manager { + if let Some(popup_manager) = &state.popup_manager() { info!("Marking all popups as dirty after Configure"); popup_manager.mark_all_popups_dirty(); } diff --git a/src/windowing/macros.rs b/adapters/src/wayland/event_handling/event_macros.rs similarity index 100% rename from src/windowing/macros.rs rename to adapters/src/wayland/event_handling/event_macros.rs diff --git a/adapters/src/wayland/event_handling/mod.rs b/adapters/src/wayland/event_handling/mod.rs new file mode 100644 index 0000000..17df0d7 --- /dev/null +++ b/adapters/src/wayland/event_handling/mod.rs @@ -0,0 +1,2 @@ +pub mod event_dispatcher; +pub mod event_macros; diff --git a/src/windowing/globals.rs b/adapters/src/wayland/globals/context.rs similarity index 95% rename from src/windowing/globals.rs rename to adapters/src/wayland/globals/context.rs index d314e4e..b75e6fd 100644 --- a/src/windowing/globals.rs +++ b/adapters/src/wayland/globals/context.rs @@ -10,20 +10,19 @@ use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_man use wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter; use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase; -use super::state::WindowState; +use crate::wayland::surfaces::surface_state::WindowState; -pub struct GlobalCtx { +pub struct GlobalContext { pub compositor: WlCompositor, pub output: WlOutput, pub layer_shell: ZwlrLayerShellV1, pub seat: WlSeat, - #[allow(dead_code)] pub xdg_wm_base: Option, pub fractional_scale_manager: Option, pub viewporter: Option, } -impl GlobalCtx { +impl GlobalContext { pub fn initialize( connection: &Connection, queue_handle: &QueueHandle, diff --git a/adapters/src/wayland/globals/mod.rs b/adapters/src/wayland/globals/mod.rs new file mode 100644 index 0000000..9efb2ab --- /dev/null +++ b/adapters/src/wayland/globals/mod.rs @@ -0,0 +1 @@ +pub mod context; diff --git a/src/windowing/proxies.rs b/adapters/src/wayland/managed_proxies.rs similarity index 98% rename from src/windowing/proxies.rs rename to adapters/src/wayland/managed_proxies.rs index 7bc32b7..95ed217 100644 --- a/src/windowing/proxies.rs +++ b/adapters/src/wayland/managed_proxies.rs @@ -13,6 +13,7 @@ pub struct ManagedWlPointer { } impl ManagedWlPointer { + #[must_use] pub const fn new(pointer: Rc, connection: Rc) -> Self { Self { pointer, @@ -50,6 +51,7 @@ pub struct ManagedWlSurface { } impl ManagedWlSurface { + #[must_use] pub const fn new(surface: Rc, connection: Rc) -> Self { Self { surface, @@ -86,6 +88,7 @@ pub struct ManagedZwlrLayerSurfaceV1 { } impl ManagedZwlrLayerSurfaceV1 { + #[must_use] pub const fn new(layer_surface: Rc, connection: Rc) -> Self { Self { layer_surface, @@ -122,6 +125,7 @@ pub struct ManagedWpFractionalScaleV1 { } impl ManagedWpFractionalScaleV1 { + #[must_use] pub const fn new( fractional_scale: Rc, connection: Rc, @@ -162,6 +166,7 @@ pub struct ManagedWpViewport { } impl ManagedWpViewport { + #[must_use] pub const fn new(viewport: Rc, connection: Rc) -> Self { Self { viewport, diff --git a/adapters/src/wayland/mod.rs b/adapters/src/wayland/mod.rs new file mode 100644 index 0000000..de30dab --- /dev/null +++ b/adapters/src/wayland/mod.rs @@ -0,0 +1,7 @@ +pub mod config; +pub mod connection; +pub mod event_handling; +pub mod globals; +pub mod managed_proxies; +pub mod shell_adapter; +pub mod surfaces; diff --git a/src/windowing/system.rs b/adapters/src/wayland/shell_adapter.rs similarity index 91% rename from src/windowing/system.rs rename to adapters/src/wayland/shell_adapter.rs index 5b72caf..0ab7dc3 100644 --- a/src/windowing/system.rs +++ b/adapters/src/wayland/shell_adapter.rs @@ -1,14 +1,14 @@ -use super::{ - config::{LayerSurfaceParams, WindowConfig}, - globals::GlobalCtx, - popup_manager::{PopupContext, PopupManager}, - state::{builder::WindowStateBuilder, WindowState}, - surface::{SurfaceCtx, SurfaceSetupParams}, +use crate::wayland::{ + config::{LayerSurfaceParams, WaylandWindowConfig}, + globals::context::GlobalContext, + surfaces::popup_manager::{PopupContext, PopupManager}, + surfaces::{surface_builder::WindowStateBuilder, surface_state::WindowState}, + surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams}, }; use crate::{ errors::{LayerShikaError, Result}, rendering::{ - egl_context::EGLContext, femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform, + egl::context::EGLContext, femtovg::main_window::FemtoVGWindow, slint_integration::platform::CustomSlintPlatform, }, }; use log::{error, info}; @@ -26,7 +26,7 @@ use wayland_client::{ Connection, EventQueue, Proxy, }; -pub struct WindowingSystem { +pub struct WaylandWindowingSystem { state: WindowState, connection: Rc, event_queue: EventQueue, @@ -34,8 +34,8 @@ pub struct WindowingSystem { popup_manager: Rc, } -impl WindowingSystem { - pub(super) fn new(config: WindowConfig) -> Result { +impl WaylandWindowingSystem { + pub fn new(config: WaylandWindowConfig) -> Result { info!("Initializing WindowingSystem"); let (connection, event_queue) = Self::init_wayland_connection()?; let (state, global_ctx, platform) = Self::init_state(config, &connection, &event_queue)?; @@ -79,11 +79,11 @@ impl WindowingSystem { } fn init_state( - config: WindowConfig, + config: WaylandWindowConfig, connection: &Connection, event_queue: &EventQueue, - ) -> Result<(WindowState, GlobalCtx, Rc)> { - let global_ctx = GlobalCtx::initialize(connection, &event_queue.handle()) + ) -> Result<(WindowState, GlobalContext, Rc)> { + let global_ctx = GlobalContext::initialize(connection, &event_queue.handle()) .map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?; let layer_surface_params = LayerSurfaceParams { @@ -177,7 +177,7 @@ impl WindowingSystem { fn initialize_renderer( surface: &Rc, display: &WlDisplay, - config: &WindowConfig, + config: &WaylandWindowConfig, ) -> Result> { let init_size = PhysicalSize::new(1, 1); @@ -293,7 +293,8 @@ impl WindowingSystem { self.state.component_instance() } - pub fn window(&self) -> Rc { + #[allow(dead_code)] + pub(crate) fn window(&self) -> Rc { self.state.window() } diff --git a/adapters/src/wayland/surfaces/dimensions.rs b/adapters/src/wayland/surfaces/dimensions.rs new file mode 100644 index 0000000..5e54b61 --- /dev/null +++ b/adapters/src/wayland/surfaces/dimensions.rs @@ -0,0 +1,17 @@ +use layer_shika_domain::surface_dimensions::SurfaceDimensions; +use slint::PhysicalSize; + +pub trait SurfaceDimensionsExt { + fn logical_size(&self) -> PhysicalSize; + fn physical_size(&self) -> PhysicalSize; +} + +impl SurfaceDimensionsExt for SurfaceDimensions { + fn logical_size(&self) -> PhysicalSize { + PhysicalSize::new(self.logical_width, self.logical_height) + } + + fn physical_size(&self) -> PhysicalSize { + PhysicalSize::new(self.physical_width, self.physical_height) + } +} diff --git a/src/windowing/surface.rs b/adapters/src/wayland/surfaces/layer_surface.rs similarity index 93% rename from src/windowing/surface.rs rename to adapters/src/wayland/surfaces/layer_surface.rs index 5cff145..38d2d25 100644 --- a/src/windowing/surface.rs +++ b/adapters/src/wayland/surfaces/layer_surface.rs @@ -1,4 +1,4 @@ -use super::{config::LayerSurfaceParams, state::WindowState}; +use crate::wayland::{config::LayerSurfaceParams, surfaces::surface_state::WindowState}; use log::info; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1}, @@ -36,7 +36,10 @@ pub struct SurfaceCtx { } impl SurfaceCtx { - pub fn setup(setup_params: &SurfaceSetupParams<'_>, params: &LayerSurfaceParams) -> Self { + pub(crate) fn setup( + setup_params: &SurfaceSetupParams<'_>, + params: &LayerSurfaceParams, + ) -> Self { let surface = Rc::new( setup_params .compositor diff --git a/adapters/src/wayland/surfaces/mod.rs b/adapters/src/wayland/surfaces/mod.rs new file mode 100644 index 0000000..7c34f74 --- /dev/null +++ b/adapters/src/wayland/surfaces/mod.rs @@ -0,0 +1,6 @@ +pub mod dimensions; +pub mod layer_surface; +pub mod popup_manager; +pub mod popup_surface; +pub mod surface_builder; +pub mod surface_state; diff --git a/src/windowing/popup_manager.rs b/adapters/src/wayland/surfaces/popup_manager.rs similarity index 95% rename from src/windowing/popup_manager.rs rename to adapters/src/wayland/surfaces/popup_manager.rs index 5c7e07c..eb41284 100644 --- a/src/windowing/popup_manager.rs +++ b/adapters/src/wayland/surfaces/popup_manager.rs @@ -1,5 +1,6 @@ use crate::errors::{LayerShikaError, Result}; -use crate::rendering::{egl_context::EGLContext, popup_window::PopupWindow}; +use crate::rendering::egl::context::EGLContext; +use crate::rendering::femtovg::popup_window::PopupWindow; use log::info; use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize}; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; @@ -14,7 +15,8 @@ use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_man use wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter; use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase; -use super::{popup::PopupSurface, state::WindowState}; +use super::popup_surface::PopupSurface; +use super::surface_state::WindowState; pub struct PopupContext { compositor: WlCompositor, @@ -28,6 +30,7 @@ pub struct PopupContext { } impl PopupContext { + #[must_use] pub const fn new( compositor: WlCompositor, xdg_wm_base: Option, @@ -62,6 +65,7 @@ pub struct PopupManager { } impl PopupManager { + #[must_use] pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self { Self { context, @@ -107,7 +111,7 @@ impl PopupManager { info!("Popup logical size: {logical_size:?}, physical size: {popup_size:?}"); - let popup_surface = PopupSurface::create(&super::popup::PopupSurfaceParams { + let popup_surface = PopupSurface::create(&super::popup_surface::PopupSurfaceParams { compositor: &self.context.compositor, xdg_wm_base, parent_layer_surface, diff --git a/src/windowing/popup.rs b/adapters/src/wayland/surfaces/popup_surface.rs similarity index 99% rename from src/windowing/popup.rs rename to adapters/src/wayland/surfaces/popup_surface.rs index 4181168..9e5a97a 100644 --- a/src/windowing/popup.rs +++ b/adapters/src/wayland/surfaces/popup_surface.rs @@ -20,7 +20,7 @@ use wayland_protocols::xdg::shell::client::{ xdg_wm_base::XdgWmBase, }; -use super::state::WindowState; +use super::surface_state::WindowState; #[allow(dead_code)] pub struct PopupSurfaceParams<'a> { diff --git a/src/windowing/state/builder.rs b/adapters/src/wayland/surfaces/surface_builder.rs similarity index 93% rename from src/windowing/state/builder.rs rename to adapters/src/wayland/surfaces/surface_builder.rs index 58d5d6d..abf2b62 100644 --- a/src/windowing/state/builder.rs +++ b/adapters/src/wayland/surfaces/surface_builder.rs @@ -1,4 +1,5 @@ use std::rc::Rc; +use std::result::Result as StdResult; use slint::{ platform::{set_platform, Platform, WindowAdapter}, PhysicalSize, PlatformError, @@ -8,15 +9,16 @@ use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::z use wayland_client::{protocol::{wl_output::WlOutput, wl_pointer::WlPointer, wl_surface::WlSurface}, Connection}; use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport; -use crate::{errors::{LayerShikaError, Result}, rendering::{femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform}}; +use crate::errors::{LayerShikaError, Result}; +use crate::rendering::femtovg::main_window::FemtoVGWindow; +use crate::rendering::slint_integration::platform::CustomSlintPlatform; -use super::WindowState; +use super::surface_state::WindowState; struct PlatformWrapper(Rc); impl Platform for PlatformWrapper { - #[allow(clippy::absolute_paths)] - fn create_window_adapter(&self) -> std::result::Result, PlatformError> { + fn create_window_adapter(&self) -> StdResult, PlatformError> { self.0.create_window_adapter() } } diff --git a/src/windowing/state/mod.rs b/adapters/src/wayland/surfaces/surface_state.rs similarity index 94% rename from src/windowing/state/mod.rs rename to adapters/src/wayland/surfaces/surface_state.rs index c8f3979..1e319f8 100644 --- a/src/windowing/state/mod.rs +++ b/adapters/src/wayland/surfaces/surface_state.rs @@ -1,5 +1,14 @@ use std::rc::Rc; -use builder::WindowStateBuilder; +use super::surface_builder::WindowStateBuilder; +use super::dimensions::SurfaceDimensionsExt; +use super::popup_manager::PopupManager; +use crate::wayland::managed_proxies::{ + ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, + ManagedWpFractionalScaleV1, ManagedWpViewport, +}; +use crate::rendering::femtovg::main_window::FemtoVGWindow; +use crate::errors::{LayerShikaError, Result}; +use layer_shika_domain::surface_dimensions::SurfaceDimensions; use log::info; use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::platform::{WindowAdapter, WindowEvent}; @@ -7,17 +16,6 @@ use slint_interpreter::ComponentInstance; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use wayland_client::{protocol::{wl_output::WlOutput, wl_surface::WlSurface}, Proxy}; use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; -use crate::rendering::femtovg_window::FemtoVGWindow; -use crate::errors::{LayerShikaError, Result}; -use crate::windowing::surface_dimensions::SurfaceDimensions; -use crate::windowing::popup_manager::PopupManager; -use crate::windowing::proxies::{ - ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, - ManagedWpFractionalScaleV1, ManagedWpViewport, -}; - -pub mod builder; -pub mod dispatches; #[derive(Debug)] enum ScalingMode { @@ -233,18 +231,14 @@ impl WindowState { &self.current_pointer_position } - pub fn window(&self) -> Rc { + pub(crate) fn window(&self) -> Rc { Rc::clone(&self.window) } - pub fn layer_surface(&self) -> Rc { + pub(crate) fn layer_surface(&self) -> Rc { Rc::clone(self.layer_surface.inner()) } - pub fn surface(&self) -> Rc { - Rc::clone(self.surface.inner()) - } - pub const fn height(&self) -> u32 { self.height } @@ -264,6 +258,10 @@ impl WindowState { &self.component_instance } + pub fn render_frame_if_dirty(&self) -> Result<()> { + self.window.render_frame_if_dirty() + } + #[allow(clippy::cast_precision_loss)] pub fn update_scale_factor(&mut self, scale_120ths: u32) { let new_scale_factor = scale_120ths as f32 / 120.0; @@ -362,13 +360,17 @@ impl WindowState { } } - pub(super) const fn clear_active_window(&mut self) { + pub const fn clear_active_window(&mut self) { self.active_window = None; } - pub(super) fn clear_active_window_if_popup(&mut self, popup_index: usize) { + pub fn clear_active_window_if_popup(&mut self, popup_index: usize) { if self.active_window == Some(ActiveWindow::Popup(popup_index)) { self.active_window = None; } } + + pub const fn popup_manager(&self) -> &Option> { + &self.popup_manager + } } diff --git a/tests/.gitkeep b/adapters/tests/.gitkeep similarity index 100% rename from tests/.gitkeep rename to adapters/tests/.gitkeep diff --git a/composition/Cargo.toml b/composition/Cargo.toml new file mode 100644 index 0000000..9b1e0c7 --- /dev/null +++ b/composition/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "layer-shika" +version = "0.1.0" +edition = "2021" +description = "A layer shell library crate with Slint UI" +license = "AGPL-3.0-or-later" +repository = "https://codeberg.org/waydeer/layer-shika" +readme = "../README.md" +keywords = ["layer-shell", "wayland", "slint", "femtovg", "smithay"] +categories = ["gui"] + +[lints] +workspace = true + +[dependencies] +layer-shika-adapters = { path = "../adapters" } +layer-shika-domain = { path = "../domain" } +thiserror = "2.0" diff --git a/composition/src/builder.rs b/composition/src/builder.rs new file mode 100644 index 0000000..89ee5c5 --- /dev/null +++ b/composition/src/builder.rs @@ -0,0 +1,79 @@ +use crate::system::WindowingSystem; +use crate::Result; +use layer_shika_adapters::platform::slint_interpreter::ComponentDefinition; +use layer_shika_domain::config::{AnchorEdges, Layer, Margins, WindowConfig}; + +pub struct NeedsComponent; +pub struct HasComponent { + component_definition: ComponentDefinition, +} + +pub struct LayerShika { + state: State, + config: WindowConfig, +} + +impl LayerShika { + #[must_use] + pub fn new(component_definition: ComponentDefinition) -> LayerShika { + LayerShika { + state: HasComponent { + component_definition, + }, + config: WindowConfig::default(), + } + } +} + +impl LayerShika { + #[must_use] + pub const fn with_height(mut self, height: u32) -> Self { + self.config.height = height; + self + } + + #[must_use] + pub const fn with_layer(mut self, layer: Layer) -> Self { + self.config.layer = layer; + self + } + + #[must_use] + pub const fn with_margin(mut self, top: i32, right: i32, bottom: i32, left: i32) -> Self { + self.config.margin = Margins { + top, + right, + bottom, + left, + }; + self + } + + #[must_use] + pub const fn with_anchor(mut self, anchor: AnchorEdges) -> Self { + self.config.anchor = anchor; + self + } + + #[must_use] + pub const fn with_exclusive_zone(mut self, zone: i32) -> Self { + self.config.exclusive_zone = zone; + self + } + + #[must_use] + pub fn with_namespace(mut self, namespace: String) -> Self { + self.config.namespace = namespace; + self + } + + #[must_use] + pub const fn with_scale_factor(mut self, scale_factor: f32) -> Self { + self.config.scale_factor = scale_factor; + self + } + + pub fn build(self) -> Result { + WindowingSystem::new(self.state.component_definition, self.config) + } +} diff --git a/composition/src/lib.rs b/composition/src/lib.rs new file mode 100644 index 0000000..2dbee9c --- /dev/null +++ b/composition/src/lib.rs @@ -0,0 +1,23 @@ +#![allow(clippy::pub_use)] + +pub mod builder; +pub mod system; + +use layer_shika_adapters::errors::LayerShikaError; +use layer_shika_domain::errors::DomainError; +use std::result::Result as StdResult; + +pub use builder::LayerShika; +pub use layer_shika_adapters::platform::{calloop, slint, slint_interpreter}; +pub use layer_shika_domain::config::AnchorEdges; + +pub type Result = StdResult; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Adapter error: {0}")] + Adapter(#[from] LayerShikaError), + + #[error("Domain error: {0}")] + Domain(#[from] DomainError), +} diff --git a/composition/src/system.rs b/composition/src/system.rs new file mode 100644 index 0000000..f1ccf7a --- /dev/null +++ b/composition/src/system.rs @@ -0,0 +1,83 @@ +use crate::Result; +use layer_shika_adapters::platform::calloop::{EventSource, InsertError, RegistrationToken}; +use layer_shika_adapters::platform::slint_interpreter::{ComponentDefinition, ComponentInstance}; +use layer_shika_adapters::wayland::{ + config::WaylandWindowConfig, + shell_adapter::WaylandWindowingSystem, +}; +use layer_shika_adapters::event_loop::calloop_adapter::{ + EventLoopAdapter, RuntimeStateAdapter, SystemAdapter, +}; +use layer_shika_domain::config::WindowConfig; +use std::result::Result as StdResult; + +pub struct EventLoopHandle { + adapter: EventLoopAdapter, +} + +impl EventLoopHandle { + pub fn insert_source( + &self, + source: S, + mut callback: F, + ) -> StdResult> + where + S: EventSource + 'static, + F: FnMut(S::Event, &mut S::Metadata, &mut RuntimeState) -> R + 'static, + { + self.adapter + .insert_source_with_adapter(source, move |event, metadata, adapter| { + let mut runtime_state = RuntimeState { adapter }; + callback(event, metadata, &mut runtime_state) + }) + } +} + +pub struct RuntimeState { + adapter: RuntimeStateAdapter, +} + +impl RuntimeState { + #[must_use] + pub fn component_instance(&self) -> &ComponentInstance { + self.adapter.component_instance() + } + + pub fn render_frame_if_dirty(&self) -> Result<()> { + Ok(self.adapter.render_frame_if_dirty()?) + } +} + +pub struct WindowingSystem { + adapter: SystemAdapter, +} + +impl WindowingSystem { + pub(crate) fn new( + component_definition: ComponentDefinition, + config: WindowConfig, + ) -> Result { + let wayland_config = WaylandWindowConfig::from_domain_config(component_definition, config); + let inner_system = WaylandWindowingSystem::new(wayland_config)?; + let adapter = SystemAdapter::new(inner_system); + + Ok(Self { adapter }) + } + + #[must_use] + pub fn event_loop_handle(&self) -> EventLoopHandle { + EventLoopHandle { + adapter: self.adapter.event_loop_handle(), + } + } + + pub fn run(&mut self) -> Result<()> { + self.adapter.run()?; + Ok(()) + } + + #[must_use] + pub const fn component_instance(&self) -> &ComponentInstance { + self.adapter.component_instance() + } +} diff --git a/domain/Cargo.toml b/domain/Cargo.toml new file mode 100644 index 0000000..8fb77ed --- /dev/null +++ b/domain/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "layer-shika-domain" +version = "0.1.0" +edition = "2021" +description = "Domain layer for layer-shika" +license = "AGPL-3.0-or-later" + +[lints] +workspace = true + +[dependencies] +thiserror = "2.0.17" diff --git a/domain/src/config.rs b/domain/src/config.rs new file mode 100644 index 0000000..2ee6459 --- /dev/null +++ b/domain/src/config.rs @@ -0,0 +1,39 @@ +#![allow(clippy::pub_use)] + +pub use crate::entities::component::UiComponentHandle; +pub use crate::value_objects::anchor::AnchorEdges; +pub use crate::value_objects::dimensions::WindowHeight; +pub use crate::value_objects::layer::Layer; +pub use crate::value_objects::margins::Margins; + +#[derive(Debug, Clone)] +pub struct WindowConfig { + pub height: u32, + pub margin: Margins, + pub exclusive_zone: i32, + pub scale_factor: f32, + pub namespace: String, + pub layer: Layer, + pub anchor: AnchorEdges, +} + +impl WindowConfig { + #[must_use] + pub fn new() -> Self { + Self { + height: 30, + margin: Margins::default(), + exclusive_zone: -1, + namespace: "layer-shika".to_owned(), + scale_factor: 1.0, + layer: Layer::default(), + anchor: AnchorEdges::default(), + } + } +} + +impl Default for WindowConfig { + fn default() -> Self { + Self::new() + } +} diff --git a/domain/src/entities/component.rs b/domain/src/entities/component.rs new file mode 100644 index 0000000..37f47fb --- /dev/null +++ b/domain/src/entities/component.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UiComponentHandle(usize); + +impl UiComponentHandle { + #[must_use] + pub const fn new(id: usize) -> Self { + Self(id) + } + + #[must_use] + pub const fn id(&self) -> usize { + self.0 + } +} diff --git a/domain/src/entities/mod.rs b/domain/src/entities/mod.rs new file mode 100644 index 0000000..0466ae4 --- /dev/null +++ b/domain/src/entities/mod.rs @@ -0,0 +1,3 @@ +pub mod component; +pub mod surface; +pub mod window; diff --git a/domain/src/entities/surface.rs b/domain/src/entities/surface.rs new file mode 100644 index 0000000..3f4a151 --- /dev/null +++ b/domain/src/entities/surface.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SurfaceHandle(usize); + +impl SurfaceHandle { + #[must_use] + pub const fn new(id: usize) -> Self { + Self(id) + } + + #[must_use] + pub const fn id(&self) -> usize { + self.0 + } +} diff --git a/domain/src/entities/window.rs b/domain/src/entities/window.rs new file mode 100644 index 0000000..ad9e0ae --- /dev/null +++ b/domain/src/entities/window.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WindowHandle(usize); + +impl WindowHandle { + #[must_use] + pub const fn new(id: usize) -> Self { + Self(id) + } + + #[must_use] + pub const fn id(&self) -> usize { + self.0 + } +} diff --git a/domain/src/errors.rs b/domain/src/errors.rs new file mode 100644 index 0000000..d3bd7cf --- /dev/null +++ b/domain/src/errors.rs @@ -0,0 +1,19 @@ +use std::result::Result as StdResult; +use thiserror::Error; + +pub type Result = StdResult; + +#[derive(Error, Debug)] +pub enum DomainError { + #[error("Configuration error: {0}")] + Configuration(String), + + #[error("Invalid dimensions: {0}")] + InvalidDimensions(String), + + #[error("Invalid input: {0}")] + InvalidInput(String), + + #[error("Calculation error: {0}")] + Calculation(String), +} diff --git a/domain/src/events/mod.rs b/domain/src/events/mod.rs new file mode 100644 index 0000000..6674ab3 --- /dev/null +++ b/domain/src/events/mod.rs @@ -0,0 +1 @@ +pub mod window_events; diff --git a/domain/src/events/window_events.rs b/domain/src/events/window_events.rs new file mode 100644 index 0000000..94a4e12 --- /dev/null +++ b/domain/src/events/window_events.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Clone)] +pub enum WindowEvent { + Resized { width: u32, height: u32 }, + ScaleChanged { scale: f32 }, + CloseRequested, + Focused, + Unfocused, + CursorMoved { x: f64, y: f64 }, + CursorEntered, + CursorLeft, + MouseButtonPressed { button: u32 }, + MouseButtonReleased { button: u32 }, + KeyPressed { key: u32 }, + KeyReleased { key: u32 }, +} diff --git a/domain/src/lib.rs b/domain/src/lib.rs new file mode 100644 index 0000000..a515119 --- /dev/null +++ b/domain/src/lib.rs @@ -0,0 +1,6 @@ +pub mod config; +pub mod entities; +pub mod errors; +pub mod events; +pub mod surface_dimensions; +pub mod value_objects; diff --git a/src/windowing/surface_dimensions.rs b/domain/src/surface_dimensions.rs similarity index 74% rename from src/windowing/surface_dimensions.rs rename to domain/src/surface_dimensions.rs index b4862e8..175c550 100644 --- a/src/windowing/surface_dimensions.rs +++ b/domain/src/surface_dimensions.rs @@ -1,5 +1,3 @@ -use slint::PhysicalSize; - #[derive(Debug, Clone, Copy)] pub struct SurfaceDimensions { pub logical_width: u32, @@ -10,6 +8,7 @@ pub struct SurfaceDimensions { } impl SurfaceDimensions { + #[must_use] #[allow( clippy::cast_possible_truncation, clippy::cast_sign_loss, @@ -28,12 +27,4 @@ impl SurfaceDimensions { buffer_scale, } } - - pub const fn logical_size(&self) -> PhysicalSize { - PhysicalSize::new(self.logical_width, self.logical_height) - } - - pub const fn physical_size(&self) -> PhysicalSize { - PhysicalSize::new(self.physical_width, self.physical_height) - } } diff --git a/domain/src/value_objects/anchor.rs b/domain/src/value_objects/anchor.rs new file mode 100644 index 0000000..cf27cd2 --- /dev/null +++ b/domain/src/value_objects/anchor.rs @@ -0,0 +1,92 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AnchorEdges(u8); + +impl AnchorEdges { + const TOP: u8 = 1 << 0; + const BOTTOM: u8 = 1 << 1; + const LEFT: u8 = 1 << 2; + const RIGHT: u8 = 1 << 3; + + #[must_use] + pub const fn new(bits: u8) -> Self { + Self(bits) + } + + #[must_use] + pub const fn empty() -> Self { + Self(0) + } + + #[must_use] + pub const fn all() -> Self { + Self(Self::TOP | Self::BOTTOM | Self::LEFT | Self::RIGHT) + } + + #[must_use] + pub const fn top_bar() -> Self { + Self(Self::TOP | Self::LEFT | Self::RIGHT) + } + + #[must_use] + pub const fn bottom_bar() -> Self { + Self(Self::BOTTOM | Self::LEFT | Self::RIGHT) + } + + #[must_use] + pub const fn with_top(mut self) -> Self { + self.0 |= Self::TOP; + self + } + + #[must_use] + pub const fn with_bottom(mut self) -> Self { + self.0 |= Self::BOTTOM; + self + } + + #[must_use] + pub const fn with_left(mut self) -> Self { + self.0 |= Self::LEFT; + self + } + + #[must_use] + pub const fn with_right(mut self) -> Self { + self.0 |= Self::RIGHT; + self + } + + #[must_use] + pub const fn has_top(&self) -> bool { + self.0 & Self::TOP != 0 + } + + #[must_use] + pub const fn has_bottom(&self) -> bool { + self.0 & Self::BOTTOM != 0 + } + + #[must_use] + pub const fn has_left(&self) -> bool { + self.0 & Self::LEFT != 0 + } + + #[must_use] + pub const fn has_right(&self) -> bool { + self.0 & Self::RIGHT != 0 + } +} + +impl Default for AnchorEdges { + fn default() -> Self { + Self::top_bar() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Anchor { + Top, + Bottom, + Left, + Right, +} diff --git a/domain/src/value_objects/dimensions.rs b/domain/src/value_objects/dimensions.rs new file mode 100644 index 0000000..a207e8d --- /dev/null +++ b/domain/src/value_objects/dimensions.rs @@ -0,0 +1,20 @@ +#[derive(Debug, Clone, Copy)] +pub struct WindowHeight(u32); + +impl WindowHeight { + #[must_use] + pub const fn new(height: u32) -> Self { + Self(height) + } + + #[must_use] + pub const fn value(&self) -> u32 { + self.0 + } +} + +impl Default for WindowHeight { + fn default() -> Self { + Self(30) + } +} diff --git a/domain/src/value_objects/layer.rs b/domain/src/value_objects/layer.rs new file mode 100644 index 0000000..69e9358 --- /dev/null +++ b/domain/src/value_objects/layer.rs @@ -0,0 +1,13 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Layer { + Background, + Bottom, + Top, + Overlay, +} + +impl Default for Layer { + fn default() -> Self { + Self::Top + } +} diff --git a/domain/src/value_objects/margins.rs b/domain/src/value_objects/margins.rs new file mode 100644 index 0000000..4097586 --- /dev/null +++ b/domain/src/value_objects/margins.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Clone, Copy, Default)] +pub struct Margins { + pub top: i32, + pub right: i32, + pub bottom: i32, + pub left: i32, +} diff --git a/domain/src/value_objects/mod.rs b/domain/src/value_objects/mod.rs new file mode 100644 index 0000000..c830a20 --- /dev/null +++ b/domain/src/value_objects/mod.rs @@ -0,0 +1,4 @@ +pub mod anchor; +pub mod dimensions; +pub mod layer; +pub mod margins; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index cc14910..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod errors; -mod reexports; -mod rendering; -mod windowing; - -pub use errors::{LayerShikaError, Result}; -pub use reexports::*; -pub use windowing::builder::WindowingSystemBuilder as LayerShika; diff --git a/src/reexports.rs b/src/reexports.rs deleted file mode 100644 index c929ae0..0000000 --- a/src/reexports.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub use slint; -pub use slint_interpreter; - -pub mod sctk { - pub use smithay_client_toolkit::reexports::*; -} - -pub mod wayland_client { - pub use wayland_client::*; -} diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs deleted file mode 100644 index 15b934b..0000000 --- a/src/rendering/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod egl_context; -pub mod femtovg_window; -pub mod popup_window; -pub mod slint_platform; diff --git a/src/windowing/builder.rs b/src/windowing/builder.rs deleted file mode 100644 index f5aec75..0000000 --- a/src/windowing/builder.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::{ - config::{Margins, WindowConfig}, - WindowingSystem, -}; -use crate::errors::Result; -use slint_interpreter::ComponentDefinition; -use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ - zwlr_layer_shell_v1::{self}, - zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity}, -}; -use std::marker::PhantomData; - -pub struct NeedsComponent; -pub struct HasComponent; - -pub struct WindowingSystemBuilder { - config: WindowConfig, - _state: PhantomData, -} - -impl WindowingSystemBuilder { - #[inline] - #[must_use] - pub fn new(component_definition: ComponentDefinition) -> WindowingSystemBuilder { - WindowingSystemBuilder { - config: WindowConfig::new(component_definition), - _state: PhantomData, - } - } -} - -impl WindowingSystemBuilder { - #[must_use] - pub const fn with_height(mut self, height: u32) -> Self { - self.config.height = height; - self - } - - #[must_use] - pub const fn with_layer(mut self, layer: zwlr_layer_shell_v1::Layer) -> Self { - self.config.layer = layer; - self - } - - #[must_use] - pub const fn with_margin(mut self, top: i32, right: i32, bottom: i32, left: i32) -> Self { - self.config.margin = Margins { - top, - right, - bottom, - left, - }; - self - } - - #[must_use] - pub const fn with_anchor(mut self, anchor: Anchor) -> Self { - self.config.anchor = anchor; - self - } - - #[must_use] - pub const fn with_keyboard_interactivity( - mut self, - interactivity: KeyboardInteractivity, - ) -> Self { - self.config.keyboard_interactivity = interactivity; - self - } - - #[must_use] - pub const fn with_exclusive_zone(mut self, zone: i32) -> Self { - self.config.exclusive_zone = zone; - self - } - - #[must_use] - pub fn with_namespace(mut self, namespace: String) -> Self { - self.config.namespace = namespace; - self - } - - #[must_use] - pub const fn with_scale_factor(mut self, scale_factor: f32) -> Self { - self.config.scale_factor = scale_factor; - self - } - - #[allow(clippy::missing_errors_doc)] - pub fn build(self) -> Result { - WindowingSystem::new(self.config) - } -} diff --git a/src/windowing/config.rs b/src/windowing/config.rs deleted file mode 100644 index e73dddb..0000000 --- a/src/windowing/config.rs +++ /dev/null @@ -1,51 +0,0 @@ -use slint_interpreter::ComponentDefinition; -use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ - zwlr_layer_shell_v1::{self}, - zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity}, -}; - -#[derive(Debug, Clone, Copy, Default)] -pub struct Margins { - pub top: i32, - pub right: i32, - pub bottom: i32, - pub left: i32, -} - -#[derive(Debug, Clone, Copy)] -pub struct LayerSurfaceParams { - pub anchor: Anchor, - pub margin: Margins, - pub exclusive_zone: i32, - pub keyboard_interactivity: KeyboardInteractivity, - pub height: u32, -} - -#[derive(Clone)] -pub struct WindowConfig { - pub height: u32, - pub layer: zwlr_layer_shell_v1::Layer, - pub margin: Margins, - pub anchor: Anchor, - pub keyboard_interactivity: KeyboardInteractivity, - pub exclusive_zone: i32, - pub scale_factor: f32, - pub namespace: String, - pub component_definition: ComponentDefinition, -} - -impl WindowConfig { - pub fn new(component_definition: ComponentDefinition) -> Self { - Self { - height: 30, - layer: zwlr_layer_shell_v1::Layer::Top, - margin: Margins::default(), - anchor: Anchor::Top | Anchor::Left | Anchor::Right, - keyboard_interactivity: KeyboardInteractivity::OnDemand, - exclusive_zone: -1, - namespace: "layer-shika".to_owned(), - scale_factor: 1.0, - component_definition, - } - } -} diff --git a/src/windowing/mod.rs b/src/windowing/mod.rs deleted file mode 100644 index 1c60b47..0000000 --- a/src/windowing/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod builder; -mod config; -mod globals; -mod macros; -mod popup; -mod popup_manager; -mod proxies; -mod state; -mod surface; -mod surface_dimensions; -mod system; - -pub use system::WindowingSystem;