refactor: migrate to reworked architecture

This commit is contained in:
drendog 2025-10-28 06:18:37 +01:00
parent e6579e1a35
commit 6353a786af
Signed by: dwenya
GPG key ID: 8DD77074645332D0
59 changed files with 861 additions and 290 deletions

17
Cargo.lock generated
View file

@ -1854,8 +1854,18 @@ dependencies = [
[[package]] [[package]]
name = "layer-shika" name = "layer-shika"
version = "0.1.0" 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 = [ dependencies = [
"glutin", "glutin",
"layer-shika-domain",
"log", "log",
"raw-window-handle", "raw-window-handle",
"slint", "slint",
@ -1866,6 +1876,13 @@ dependencies = [
"wayland-protocols", "wayland-protocols",
] ]
[[package]]
name = "layer-shika-domain"
version = "0.1.0"
dependencies = [
"thiserror 2.0.17",
]
[[package]] [[package]]
name = "lazy-bytes-cast" name = "lazy-bytes-cast"
version = "5.0.1" version = "5.0.1"

View file

@ -1,48 +1,31 @@
[package] [workspace]
name = "layer-shika" resolver = "2"
version = "0.1.0" members = ["domain", "adapters", "composition"]
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.clippy] [workspace.lints.clippy]
all = { level = "warn", priority = -1 } all = { level = "deny", priority = -1 }
cargo = { level = "warn", priority = -1 } pedantic = { level = "deny", priority = -1 }
nursery = { level = "warn", priority = -1 } disallowed_types = "deny"
pedantic = { level = "warn", priority = -1 } disallowed_methods = "deny"
clone_on_ref_ptr = "warn" pub_use = "deny"
multiple-crate-versions = "allow" unwrap_used = "deny"
module_name_repetitions = "allow" expect_used = "deny"
unwrap_used = "warn" 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" absolute_paths = "deny"
missing_errors_doc = "allow"
[dependencies] missing_panics_doc = "allow"
glutin = { version = "0.32.3", default-features = false, features = [ must_use_candidate = "allow"
"wayland", uninlined_format_args = "allow"
] }
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"] }

29
adapters/Cargo.toml Normal file
View file

@ -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"] }

View file

@ -1,3 +1,4 @@
use layer_shika_domain::errors::DomainError;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use thiserror::Error; use thiserror::Error;
use wayland_client::backend::WaylandError; use wayland_client::backend::WaylandError;
@ -6,6 +7,9 @@ pub type Result<T> = StdResult<T, LayerShikaError>;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum LayerShikaError { pub enum LayerShikaError {
#[error("Domain error: {0}")]
Domain(#[from] DomainError),
#[error("Failed to connect to Wayland: {0}")] #[error("Failed to connect to Wayland: {0}")]
WaylandConnection(#[from] wayland_client::ConnectError), WaylandConnection(#[from] wayland_client::ConnectError),

View file

@ -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<S, F, R>(
&self,
source: S,
mut callback: F,
) -> StdResult<RegistrationToken, InsertError<S>>
where
S: EventSource<Ret = R> + '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()
}
}

View file

@ -0,0 +1 @@
pub mod calloop_adapter;

18
adapters/src/lib.rs Normal file
View file

@ -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,
};
}
}

View file

@ -40,31 +40,37 @@ pub struct EGLContextBuilder {
} }
impl EGLContextBuilder { impl EGLContextBuilder {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
#[must_use]
pub fn with_display_id(mut self, display_id: ObjectId) -> Self { pub fn with_display_id(mut self, display_id: ObjectId) -> Self {
self.display_id = Some(display_id); self.display_id = Some(display_id);
self self
} }
#[must_use]
pub fn with_surface_id(mut self, surface_id: ObjectId) -> Self { pub fn with_surface_id(mut self, surface_id: ObjectId) -> Self {
self.surface_id = Some(surface_id); self.surface_id = Some(surface_id);
self self
} }
#[must_use]
pub const fn with_size(mut self, size: PhysicalSize) -> Self { pub const fn with_size(mut self, size: PhysicalSize) -> Self {
self.size = Some(size); self.size = Some(size);
self self
} }
#[must_use]
#[allow(dead_code)] #[allow(dead_code)]
pub const fn with_config_template(mut self, config_template: ConfigTemplateBuilder) -> Self { pub const fn with_config_template(mut self, config_template: ConfigTemplateBuilder) -> Self {
self.config_template = Some(config_template); self.config_template = Some(config_template);
self self
} }
#[must_use]
#[allow(dead_code)] #[allow(dead_code)]
pub const fn with_context_attributes( pub const fn with_context_attributes(
mut self, mut self,
@ -110,6 +116,7 @@ impl EGLContextBuilder {
} }
impl EGLContext { impl EGLContext {
#[must_use]
pub fn builder() -> EGLContextBuilder { pub fn builder() -> EGLContextBuilder {
EGLContextBuilder::new() EGLContextBuilder::new()
} }

View file

@ -0,0 +1 @@
pub mod context;

View file

@ -22,6 +22,7 @@ pub struct FemtoVGWindow {
} }
impl FemtoVGWindow { impl FemtoVGWindow {
#[must_use]
pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> { pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> {
Rc::new_cyclic(|weak_self| { Rc::new_cyclic(|weak_self| {
let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>); let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>);

View file

@ -0,0 +1,2 @@
pub mod main_window;
pub mod popup_window;

View file

@ -8,7 +8,7 @@ use slint::{
use std::cell::Cell; use std::cell::Cell;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use super::femtovg_window::RenderState; use super::main_window::RenderState;
#[allow(dead_code)] #[allow(dead_code)]
pub struct PopupWindow { pub struct PopupWindow {
@ -21,6 +21,7 @@ pub struct PopupWindow {
#[allow(dead_code)] #[allow(dead_code)]
impl PopupWindow { impl PopupWindow {
#[must_use]
pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> { pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> {
Rc::new_cyclic(|weak_self| { Rc::new_cyclic(|weak_self| {
let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>); let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>);

View file

@ -0,0 +1,3 @@
pub mod egl;
pub mod femtovg;
pub mod slint_integration;

View file

@ -0,0 +1 @@
pub mod platform;

View file

@ -5,7 +5,7 @@ use slint::{
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use super::femtovg_window::FemtoVGWindow; use crate::rendering::femtovg::main_window::FemtoVGWindow;
type PopupCreator = dyn Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError>; type PopupCreator = dyn Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError>;
@ -16,6 +16,7 @@ pub struct CustomSlintPlatform {
} }
impl CustomSlintPlatform { impl CustomSlintPlatform {
#[must_use]
pub fn new(window: &Rc<FemtoVGWindow>) -> Self { pub fn new(window: &Rc<FemtoVGWindow>) -> Self {
Self { Self {
main_window: Rc::downgrade(window), main_window: Rc::downgrade(window),

View file

@ -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
}

View file

@ -0,0 +1,10 @@
use crate::errors::{LayerShikaError, Result};
use std::rc::Rc;
use wayland_client::{Connection, EventQueue};
pub fn initialize_wayland<S>() -> Result<(Rc<Connection>, EventQueue<S>)> {
let connection =
Rc::new(Connection::connect_to_env().map_err(LayerShikaError::WaylandConnection)?);
let event_queue = connection.new_event_queue();
Ok((connection, event_queue))
}

View file

@ -1,4 +1,4 @@
use super::WindowState; use crate::wayland::surfaces::surface_state::WindowState;
use crate::impl_empty_dispatch; use crate::impl_empty_dispatch;
use log::info; use log::info;
use slint::{ use slint::{
@ -271,14 +271,14 @@ impl Dispatch<XdgPopup, ()> for WindowState {
info!("XdgPopup dismissed by compositor"); info!("XdgPopup dismissed by compositor");
let popup_id = xdg_popup.id(); let popup_id = xdg_popup.id();
let popup_index = state let popup_index = state
.popup_manager .popup_manager()
.as_ref() .as_ref()
.and_then(|pm| pm.find_popup_index_by_xdg_popup_id(&popup_id)); .and_then(|pm| pm.find_popup_index_by_xdg_popup_id(&popup_id));
if let Some(index) = popup_index { if let Some(index) = popup_index {
info!("Destroying popup at index {index}"); info!("Destroying popup at index {index}");
state.clear_active_window_if_popup(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); popup_manager.destroy_popup(index);
} }
} }
@ -304,7 +304,7 @@ impl Dispatch<XdgSurface, ()> for WindowState {
info!("XdgSurface Configure received, sending ack with serial {serial}"); info!("XdgSurface Configure received, sending ack with serial {serial}");
xdg_surface.ack_configure(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"); info!("Marking all popups as dirty after Configure");
popup_manager.mark_all_popups_dirty(); popup_manager.mark_all_popups_dirty();
} }

View file

@ -0,0 +1,2 @@
pub mod event_dispatcher;
pub mod event_macros;

View file

@ -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::wp::viewporter::client::wp_viewporter::WpViewporter;
use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase; 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 compositor: WlCompositor,
pub output: WlOutput, pub output: WlOutput,
pub layer_shell: ZwlrLayerShellV1, pub layer_shell: ZwlrLayerShellV1,
pub seat: WlSeat, pub seat: WlSeat,
#[allow(dead_code)]
pub xdg_wm_base: Option<XdgWmBase>, pub xdg_wm_base: Option<XdgWmBase>,
pub fractional_scale_manager: Option<WpFractionalScaleManagerV1>, pub fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
pub viewporter: Option<WpViewporter>, pub viewporter: Option<WpViewporter>,
} }
impl GlobalCtx { impl GlobalContext {
pub fn initialize( pub fn initialize(
connection: &Connection, connection: &Connection,
queue_handle: &QueueHandle<WindowState>, queue_handle: &QueueHandle<WindowState>,

View file

@ -0,0 +1 @@
pub mod context;

View file

@ -13,6 +13,7 @@ pub struct ManagedWlPointer {
} }
impl ManagedWlPointer { impl ManagedWlPointer {
#[must_use]
pub const fn new(pointer: Rc<WlPointer>, connection: Rc<Connection>) -> Self { pub const fn new(pointer: Rc<WlPointer>, connection: Rc<Connection>) -> Self {
Self { Self {
pointer, pointer,
@ -50,6 +51,7 @@ pub struct ManagedWlSurface {
} }
impl ManagedWlSurface { impl ManagedWlSurface {
#[must_use]
pub const fn new(surface: Rc<WlSurface>, connection: Rc<Connection>) -> Self { pub const fn new(surface: Rc<WlSurface>, connection: Rc<Connection>) -> Self {
Self { Self {
surface, surface,
@ -86,6 +88,7 @@ pub struct ManagedZwlrLayerSurfaceV1 {
} }
impl ManagedZwlrLayerSurfaceV1 { impl ManagedZwlrLayerSurfaceV1 {
#[must_use]
pub const fn new(layer_surface: Rc<ZwlrLayerSurfaceV1>, connection: Rc<Connection>) -> Self { pub const fn new(layer_surface: Rc<ZwlrLayerSurfaceV1>, connection: Rc<Connection>) -> Self {
Self { Self {
layer_surface, layer_surface,
@ -122,6 +125,7 @@ pub struct ManagedWpFractionalScaleV1 {
} }
impl ManagedWpFractionalScaleV1 { impl ManagedWpFractionalScaleV1 {
#[must_use]
pub const fn new( pub const fn new(
fractional_scale: Rc<WpFractionalScaleV1>, fractional_scale: Rc<WpFractionalScaleV1>,
connection: Rc<Connection>, connection: Rc<Connection>,
@ -162,6 +166,7 @@ pub struct ManagedWpViewport {
} }
impl ManagedWpViewport { impl ManagedWpViewport {
#[must_use]
pub const fn new(viewport: Rc<WpViewport>, connection: Rc<Connection>) -> Self { pub const fn new(viewport: Rc<WpViewport>, connection: Rc<Connection>) -> Self {
Self { Self {
viewport, viewport,

View file

@ -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;

View file

@ -1,14 +1,14 @@
use super::{ use crate::wayland::{
config::{LayerSurfaceParams, WindowConfig}, config::{LayerSurfaceParams, WaylandWindowConfig},
globals::GlobalCtx, globals::context::GlobalContext,
popup_manager::{PopupContext, PopupManager}, surfaces::popup_manager::{PopupContext, PopupManager},
state::{builder::WindowStateBuilder, WindowState}, surfaces::{surface_builder::WindowStateBuilder, surface_state::WindowState},
surface::{SurfaceCtx, SurfaceSetupParams}, surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
}; };
use crate::{ use crate::{
errors::{LayerShikaError, Result}, errors::{LayerShikaError, Result},
rendering::{ 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}; use log::{error, info};
@ -26,7 +26,7 @@ use wayland_client::{
Connection, EventQueue, Proxy, Connection, EventQueue, Proxy,
}; };
pub struct WindowingSystem { pub struct WaylandWindowingSystem {
state: WindowState, state: WindowState,
connection: Rc<Connection>, connection: Rc<Connection>,
event_queue: EventQueue<WindowState>, event_queue: EventQueue<WindowState>,
@ -34,8 +34,8 @@ pub struct WindowingSystem {
popup_manager: Rc<PopupManager>, popup_manager: Rc<PopupManager>,
} }
impl WindowingSystem { impl WaylandWindowingSystem {
pub(super) fn new(config: WindowConfig) -> Result<Self> { pub fn new(config: WaylandWindowConfig) -> Result<Self> {
info!("Initializing WindowingSystem"); info!("Initializing WindowingSystem");
let (connection, event_queue) = Self::init_wayland_connection()?; let (connection, event_queue) = Self::init_wayland_connection()?;
let (state, global_ctx, platform) = Self::init_state(config, &connection, &event_queue)?; let (state, global_ctx, platform) = Self::init_state(config, &connection, &event_queue)?;
@ -79,11 +79,11 @@ impl WindowingSystem {
} }
fn init_state( fn init_state(
config: WindowConfig, config: WaylandWindowConfig,
connection: &Connection, connection: &Connection,
event_queue: &EventQueue<WindowState>, event_queue: &EventQueue<WindowState>,
) -> Result<(WindowState, GlobalCtx, Rc<CustomSlintPlatform>)> { ) -> Result<(WindowState, GlobalContext, Rc<CustomSlintPlatform>)> {
let global_ctx = GlobalCtx::initialize(connection, &event_queue.handle()) let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?; .map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
let layer_surface_params = LayerSurfaceParams { let layer_surface_params = LayerSurfaceParams {
@ -177,7 +177,7 @@ impl WindowingSystem {
fn initialize_renderer( fn initialize_renderer(
surface: &Rc<WlSurface>, surface: &Rc<WlSurface>,
display: &WlDisplay, display: &WlDisplay,
config: &WindowConfig, config: &WaylandWindowConfig,
) -> Result<Rc<FemtoVGWindow>> { ) -> Result<Rc<FemtoVGWindow>> {
let init_size = PhysicalSize::new(1, 1); let init_size = PhysicalSize::new(1, 1);
@ -293,7 +293,8 @@ impl WindowingSystem {
self.state.component_instance() self.state.component_instance()
} }
pub fn window(&self) -> Rc<FemtoVGWindow> { #[allow(dead_code)]
pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
self.state.window() self.state.window()
} }

View file

@ -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)
}
}

View file

@ -1,4 +1,4 @@
use super::{config::LayerSurfaceParams, state::WindowState}; use crate::wayland::{config::LayerSurfaceParams, surfaces::surface_state::WindowState};
use log::info; use log::info;
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1}, zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
@ -36,7 +36,10 @@ pub struct SurfaceCtx {
} }
impl 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( let surface = Rc::new(
setup_params setup_params
.compositor .compositor

View file

@ -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;

View file

@ -1,5 +1,6 @@
use crate::errors::{LayerShikaError, Result}; 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 log::info;
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize}; use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize};
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; 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::wp::viewporter::client::wp_viewporter::WpViewporter;
use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase; 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 { pub struct PopupContext {
compositor: WlCompositor, compositor: WlCompositor,
@ -28,6 +30,7 @@ pub struct PopupContext {
} }
impl PopupContext { impl PopupContext {
#[must_use]
pub const fn new( pub const fn new(
compositor: WlCompositor, compositor: WlCompositor,
xdg_wm_base: Option<XdgWmBase>, xdg_wm_base: Option<XdgWmBase>,
@ -62,6 +65,7 @@ pub struct PopupManager {
} }
impl PopupManager { impl PopupManager {
#[must_use]
pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self { pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self {
Self { Self {
context, context,
@ -107,7 +111,7 @@ impl PopupManager {
info!("Popup logical size: {logical_size:?}, physical size: {popup_size:?}"); 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, compositor: &self.context.compositor,
xdg_wm_base, xdg_wm_base,
parent_layer_surface, parent_layer_surface,

View file

@ -20,7 +20,7 @@ use wayland_protocols::xdg::shell::client::{
xdg_wm_base::XdgWmBase, xdg_wm_base::XdgWmBase,
}; };
use super::state::WindowState; use super::surface_state::WindowState;
#[allow(dead_code)] #[allow(dead_code)]
pub struct PopupSurfaceParams<'a> { pub struct PopupSurfaceParams<'a> {

View file

@ -1,4 +1,5 @@
use std::rc::Rc; use std::rc::Rc;
use std::result::Result as StdResult;
use slint::{ use slint::{
platform::{set_platform, Platform, WindowAdapter}, platform::{set_platform, Platform, WindowAdapter},
PhysicalSize, PlatformError, 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_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::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport; 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<CustomSlintPlatform>); struct PlatformWrapper(Rc<CustomSlintPlatform>);
impl Platform for PlatformWrapper { impl Platform for PlatformWrapper {
#[allow(clippy::absolute_paths)] fn create_window_adapter(&self) -> StdResult<Rc<dyn WindowAdapter>, PlatformError> {
fn create_window_adapter(&self) -> std::result::Result<Rc<dyn WindowAdapter>, PlatformError> {
self.0.create_window_adapter() self.0.create_window_adapter()
} }
} }

View file

@ -1,5 +1,14 @@
use std::rc::Rc; 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 log::info;
use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
use slint::platform::{WindowAdapter, WindowEvent}; 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 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_client::{protocol::{wl_output::WlOutput, wl_surface::WlSurface}, Proxy};
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; 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)] #[derive(Debug)]
enum ScalingMode { enum ScalingMode {
@ -233,18 +231,14 @@ impl WindowState {
&self.current_pointer_position &self.current_pointer_position
} }
pub fn window(&self) -> Rc<FemtoVGWindow> { pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
Rc::clone(&self.window) Rc::clone(&self.window)
} }
pub fn layer_surface(&self) -> Rc<ZwlrLayerSurfaceV1> { pub(crate) fn layer_surface(&self) -> Rc<ZwlrLayerSurfaceV1> {
Rc::clone(self.layer_surface.inner()) Rc::clone(self.layer_surface.inner())
} }
pub fn surface(&self) -> Rc<WlSurface> {
Rc::clone(self.surface.inner())
}
pub const fn height(&self) -> u32 { pub const fn height(&self) -> u32 {
self.height self.height
} }
@ -264,6 +258,10 @@ impl WindowState {
&self.component_instance &self.component_instance
} }
pub fn render_frame_if_dirty(&self) -> Result<()> {
self.window.render_frame_if_dirty()
}
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
pub fn update_scale_factor(&mut self, scale_120ths: u32) { pub fn update_scale_factor(&mut self, scale_120ths: u32) {
let new_scale_factor = scale_120ths as f32 / 120.0; 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; 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)) { if self.active_window == Some(ActiveWindow::Popup(popup_index)) {
self.active_window = None; self.active_window = None;
} }
} }
pub const fn popup_manager(&self) -> &Option<Rc<PopupManager>> {
&self.popup_manager
}
} }

18
composition/Cargo.toml Normal file
View file

@ -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"

View file

@ -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: State,
config: WindowConfig,
}
impl LayerShika<NeedsComponent> {
#[must_use]
pub fn new(component_definition: ComponentDefinition) -> LayerShika<HasComponent> {
LayerShika {
state: HasComponent {
component_definition,
},
config: WindowConfig::default(),
}
}
}
impl LayerShika<HasComponent> {
#[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> {
WindowingSystem::new(self.state.component_definition, self.config)
}
}

23
composition/src/lib.rs Normal file
View file

@ -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<T> = StdResult<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Adapter error: {0}")]
Adapter(#[from] LayerShikaError),
#[error("Domain error: {0}")]
Domain(#[from] DomainError),
}

83
composition/src/system.rs Normal file
View file

@ -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<S, F, R>(
&self,
source: S,
mut callback: F,
) -> StdResult<RegistrationToken, InsertError<S>>
where
S: EventSource<Ret = R> + '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<Self> {
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()
}
}

12
domain/Cargo.toml Normal file
View file

@ -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"

39
domain/src/config.rs Normal file
View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,3 @@
pub mod component;
pub mod surface;
pub mod window;

View file

@ -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
}
}

View file

@ -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
}
}

19
domain/src/errors.rs Normal file
View file

@ -0,0 +1,19 @@
use std::result::Result as StdResult;
use thiserror::Error;
pub type Result<T> = StdResult<T, DomainError>;
#[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),
}

1
domain/src/events/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod window_events;

View file

@ -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 },
}

6
domain/src/lib.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod config;
pub mod entities;
pub mod errors;
pub mod events;
pub mod surface_dimensions;
pub mod value_objects;

View file

@ -1,5 +1,3 @@
use slint::PhysicalSize;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct SurfaceDimensions { pub struct SurfaceDimensions {
pub logical_width: u32, pub logical_width: u32,
@ -10,6 +8,7 @@ pub struct SurfaceDimensions {
} }
impl SurfaceDimensions { impl SurfaceDimensions {
#[must_use]
#[allow( #[allow(
clippy::cast_possible_truncation, clippy::cast_possible_truncation,
clippy::cast_sign_loss, clippy::cast_sign_loss,
@ -28,12 +27,4 @@ impl SurfaceDimensions {
buffer_scale, 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)
}
} }

View file

@ -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,
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,7 @@
#[derive(Debug, Clone, Copy, Default)]
pub struct Margins {
pub top: i32,
pub right: i32,
pub bottom: i32,
pub left: i32,
}

View file

@ -0,0 +1,4 @@
pub mod anchor;
pub mod dimensions;
pub mod layer;
pub mod margins;

View file

@ -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;

View file

@ -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::*;
}

View file

@ -1,4 +0,0 @@
pub mod egl_context;
pub mod femtovg_window;
pub mod popup_window;
pub mod slint_platform;

View file

@ -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<State = NeedsComponent> {
config: WindowConfig,
_state: PhantomData<State>,
}
impl WindowingSystemBuilder<NeedsComponent> {
#[inline]
#[must_use]
pub fn new(component_definition: ComponentDefinition) -> WindowingSystemBuilder<HasComponent> {
WindowingSystemBuilder {
config: WindowConfig::new(component_definition),
_state: PhantomData,
}
}
}
impl WindowingSystemBuilder<HasComponent> {
#[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> {
WindowingSystem::new(self.config)
}
}

View file

@ -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,
}
}
}

View file

@ -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;