mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-03 12:24:23 +00:00
refactor: migrate to reworked architecture
This commit is contained in:
parent
e6579e1a35
commit
6353a786af
59 changed files with 861 additions and 290 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
75
Cargo.toml
75
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"
|
||||
|
|
|
|||
29
adapters/Cargo.toml
Normal file
29
adapters/Cargo.toml
Normal 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"] }
|
||||
|
|
@ -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<T> = StdResult<T, LayerShikaError>;
|
|||
|
||||
#[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),
|
||||
|
||||
79
adapters/src/event_loop/calloop_adapter.rs
Normal file
79
adapters/src/event_loop/calloop_adapter.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
1
adapters/src/event_loop/mod.rs
Normal file
1
adapters/src/event_loop/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod calloop_adapter;
|
||||
18
adapters/src/lib.rs
Normal file
18
adapters/src/lib.rs
Normal 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
1
adapters/src/rendering/egl/mod.rs
Normal file
1
adapters/src/rendering/egl/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod context;
|
||||
|
|
@ -22,6 +22,7 @@ pub struct FemtoVGWindow {
|
|||
}
|
||||
|
||||
impl FemtoVGWindow {
|
||||
#[must_use]
|
||||
pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> {
|
||||
Rc::new_cyclic(|weak_self| {
|
||||
let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>);
|
||||
2
adapters/src/rendering/femtovg/mod.rs
Normal file
2
adapters/src/rendering/femtovg/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod main_window;
|
||||
pub mod popup_window;
|
||||
|
|
@ -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<Self> {
|
||||
Rc::new_cyclic(|weak_self| {
|
||||
let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>);
|
||||
3
adapters/src/rendering/mod.rs
Normal file
3
adapters/src/rendering/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod egl;
|
||||
pub mod femtovg;
|
||||
pub mod slint_integration;
|
||||
1
adapters/src/rendering/slint_integration/mod.rs
Normal file
1
adapters/src/rendering/slint_integration/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod platform;
|
||||
|
|
@ -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<Rc<dyn WindowAdapter>, PlatformError>;
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ pub struct CustomSlintPlatform {
|
|||
}
|
||||
|
||||
impl CustomSlintPlatform {
|
||||
#[must_use]
|
||||
pub fn new(window: &Rc<FemtoVGWindow>) -> Self {
|
||||
Self {
|
||||
main_window: Rc::downgrade(window),
|
||||
76
adapters/src/wayland/config.rs
Normal file
76
adapters/src/wayland/config.rs
Normal 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
|
||||
}
|
||||
10
adapters/src/wayland/connection.rs
Normal file
10
adapters/src/wayland/connection.rs
Normal 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))
|
||||
}
|
||||
|
|
@ -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<XdgPopup, ()> 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<XdgSurface, ()> 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();
|
||||
}
|
||||
2
adapters/src/wayland/event_handling/mod.rs
Normal file
2
adapters/src/wayland/event_handling/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod event_dispatcher;
|
||||
pub mod event_macros;
|
||||
|
|
@ -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<XdgWmBase>,
|
||||
pub fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
|
||||
pub viewporter: Option<WpViewporter>,
|
||||
}
|
||||
|
||||
impl GlobalCtx {
|
||||
impl GlobalContext {
|
||||
pub fn initialize(
|
||||
connection: &Connection,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
1
adapters/src/wayland/globals/mod.rs
Normal file
1
adapters/src/wayland/globals/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod context;
|
||||
|
|
@ -13,6 +13,7 @@ pub struct ManagedWlPointer {
|
|||
}
|
||||
|
||||
impl ManagedWlPointer {
|
||||
#[must_use]
|
||||
pub const fn new(pointer: Rc<WlPointer>, connection: Rc<Connection>) -> Self {
|
||||
Self {
|
||||
pointer,
|
||||
|
|
@ -50,6 +51,7 @@ pub struct ManagedWlSurface {
|
|||
}
|
||||
|
||||
impl ManagedWlSurface {
|
||||
#[must_use]
|
||||
pub const fn new(surface: Rc<WlSurface>, connection: Rc<Connection>) -> Self {
|
||||
Self {
|
||||
surface,
|
||||
|
|
@ -86,6 +88,7 @@ pub struct ManagedZwlrLayerSurfaceV1 {
|
|||
}
|
||||
|
||||
impl ManagedZwlrLayerSurfaceV1 {
|
||||
#[must_use]
|
||||
pub const fn new(layer_surface: Rc<ZwlrLayerSurfaceV1>, connection: Rc<Connection>) -> Self {
|
||||
Self {
|
||||
layer_surface,
|
||||
|
|
@ -122,6 +125,7 @@ pub struct ManagedWpFractionalScaleV1 {
|
|||
}
|
||||
|
||||
impl ManagedWpFractionalScaleV1 {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
fractional_scale: Rc<WpFractionalScaleV1>,
|
||||
connection: Rc<Connection>,
|
||||
|
|
@ -162,6 +166,7 @@ pub struct ManagedWpViewport {
|
|||
}
|
||||
|
||||
impl ManagedWpViewport {
|
||||
#[must_use]
|
||||
pub const fn new(viewport: Rc<WpViewport>, connection: Rc<Connection>) -> Self {
|
||||
Self {
|
||||
viewport,
|
||||
7
adapters/src/wayland/mod.rs
Normal file
7
adapters/src/wayland/mod.rs
Normal 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;
|
||||
|
|
@ -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<Connection>,
|
||||
event_queue: EventQueue<WindowState>,
|
||||
|
|
@ -34,8 +34,8 @@ pub struct WindowingSystem {
|
|||
popup_manager: Rc<PopupManager>,
|
||||
}
|
||||
|
||||
impl WindowingSystem {
|
||||
pub(super) fn new(config: WindowConfig) -> Result<Self> {
|
||||
impl WaylandWindowingSystem {
|
||||
pub fn new(config: WaylandWindowConfig) -> Result<Self> {
|
||||
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<WindowState>,
|
||||
) -> Result<(WindowState, GlobalCtx, Rc<CustomSlintPlatform>)> {
|
||||
let global_ctx = GlobalCtx::initialize(connection, &event_queue.handle())
|
||||
) -> Result<(WindowState, GlobalContext, Rc<CustomSlintPlatform>)> {
|
||||
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<WlSurface>,
|
||||
display: &WlDisplay,
|
||||
config: &WindowConfig,
|
||||
config: &WaylandWindowConfig,
|
||||
) -> Result<Rc<FemtoVGWindow>> {
|
||||
let init_size = PhysicalSize::new(1, 1);
|
||||
|
||||
|
|
@ -293,7 +293,8 @@ impl WindowingSystem {
|
|||
self.state.component_instance()
|
||||
}
|
||||
|
||||
pub fn window(&self) -> Rc<FemtoVGWindow> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
|
||||
self.state.window()
|
||||
}
|
||||
|
||||
17
adapters/src/wayland/surfaces/dimensions.rs
Normal file
17
adapters/src/wayland/surfaces/dimensions.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
6
adapters/src/wayland/surfaces/mod.rs
Normal file
6
adapters/src/wayland/surfaces/mod.rs
Normal 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;
|
||||
|
|
@ -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<XdgWmBase>,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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> {
|
||||
|
|
@ -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<CustomSlintPlatform>);
|
||||
|
||||
impl Platform for PlatformWrapper {
|
||||
#[allow(clippy::absolute_paths)]
|
||||
fn create_window_adapter(&self) -> std::result::Result<Rc<dyn WindowAdapter>, PlatformError> {
|
||||
fn create_window_adapter(&self) -> StdResult<Rc<dyn WindowAdapter>, PlatformError> {
|
||||
self.0.create_window_adapter()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FemtoVGWindow> {
|
||||
pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
|
||||
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())
|
||||
}
|
||||
|
||||
pub fn surface(&self) -> Rc<WlSurface> {
|
||||
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<Rc<PopupManager>> {
|
||||
&self.popup_manager
|
||||
}
|
||||
}
|
||||
18
composition/Cargo.toml
Normal file
18
composition/Cargo.toml
Normal 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"
|
||||
79
composition/src/builder.rs
Normal file
79
composition/src/builder.rs
Normal 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
23
composition/src/lib.rs
Normal 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
83
composition/src/system.rs
Normal 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
12
domain/Cargo.toml
Normal 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
39
domain/src/config.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
14
domain/src/entities/component.rs
Normal file
14
domain/src/entities/component.rs
Normal 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
|
||||
}
|
||||
}
|
||||
3
domain/src/entities/mod.rs
Normal file
3
domain/src/entities/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod component;
|
||||
pub mod surface;
|
||||
pub mod window;
|
||||
14
domain/src/entities/surface.rs
Normal file
14
domain/src/entities/surface.rs
Normal 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
|
||||
}
|
||||
}
|
||||
14
domain/src/entities/window.rs
Normal file
14
domain/src/entities/window.rs
Normal 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
19
domain/src/errors.rs
Normal 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
1
domain/src/events/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod window_events;
|
||||
15
domain/src/events/window_events.rs
Normal file
15
domain/src/events/window_events.rs
Normal 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
6
domain/src/lib.rs
Normal 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;
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
92
domain/src/value_objects/anchor.rs
Normal file
92
domain/src/value_objects/anchor.rs
Normal 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,
|
||||
}
|
||||
20
domain/src/value_objects/dimensions.rs
Normal file
20
domain/src/value_objects/dimensions.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
13
domain/src/value_objects/layer.rs
Normal file
13
domain/src/value_objects/layer.rs
Normal 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
|
||||
}
|
||||
}
|
||||
7
domain/src/value_objects/margins.rs
Normal file
7
domain/src/value_objects/margins.rs
Normal 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,
|
||||
}
|
||||
4
domain/src/value_objects/mod.rs
Normal file
4
domain/src/value_objects/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod anchor;
|
||||
pub mod dimensions;
|
||||
pub mod layer;
|
||||
pub mod margins;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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::*;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
pub mod egl_context;
|
||||
pub mod femtovg_window;
|
||||
pub mod popup_window;
|
||||
pub mod slint_platform;
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
Loading…
Add table
Reference in a new issue