mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-03 09:04:24 +00:00
refactor: minor architecture improvement
This commit is contained in:
parent
e8d636798d
commit
18d1391dee
33 changed files with 642 additions and 420 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1868,6 +1868,7 @@ dependencies = [
|
|||
"layer-shika-domain",
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
"slab",
|
||||
"slint",
|
||||
"slint-interpreter",
|
||||
"smithay-client-toolkit 0.20.0",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ glutin = { version = "0.32.3", default-features = false, features = [
|
|||
] }
|
||||
log = "0.4.28"
|
||||
raw-window-handle = "0.6.2"
|
||||
slab = "0.4"
|
||||
slint = { version = "1.14.1", default-features = false, features = [
|
||||
"compat-1-2",
|
||||
"renderer-femtovg",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ glutin.workspace = true
|
|||
layer-shika-domain.workspace = true
|
||||
log.workspace = true
|
||||
raw-window-handle.workspace = true
|
||||
slab.workspace = true
|
||||
slint.workspace = true
|
||||
slint-interpreter.workspace = true
|
||||
smithay-client-toolkit.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,51 +1,172 @@
|
|||
use layer_shika_domain::errors::DomainError;
|
||||
use slint::{PlatformError, platform::SetPlatformError};
|
||||
use smithay_client_toolkit::reexports::calloop;
|
||||
use std::error::Error;
|
||||
use std::result::Result as StdResult;
|
||||
use thiserror::Error;
|
||||
use wayland_client::backend::WaylandError;
|
||||
use wayland_client::{
|
||||
ConnectError, DispatchError,
|
||||
globals::{BindError, GlobalError},
|
||||
};
|
||||
|
||||
pub type Result<T> = StdResult<T, LayerShikaError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LayerShikaError {
|
||||
#[error("Domain error: {0}")]
|
||||
Domain(#[from] DomainError),
|
||||
pub enum RenderingError {
|
||||
#[error("failed to swap buffers")]
|
||||
SwapBuffers {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("Failed to connect to Wayland: {0}")]
|
||||
WaylandConnection(#[from] wayland_client::ConnectError),
|
||||
#[error("failed to ensure context current")]
|
||||
EnsureContextCurrent {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("Failed to initialize Wayland globals: {0}")]
|
||||
GlobalInitialization(String),
|
||||
|
||||
#[error("Failed to dispatch Wayland event: {0}")]
|
||||
WaylandDispatch(String),
|
||||
|
||||
#[error("Failed to create EGL context: {0}")]
|
||||
EGLContextCreation(String),
|
||||
|
||||
#[error("Failed to create FemtoVG renderer: {0}")]
|
||||
FemtoVGRendererCreation(String),
|
||||
|
||||
#[error("Failed to create Slint component: {0}")]
|
||||
SlintComponentCreation(String),
|
||||
|
||||
#[error("Failed to run event loop: {0}")]
|
||||
EventLoop(String),
|
||||
|
||||
#[error("Window configuration error: {0}")]
|
||||
WindowConfiguration(String),
|
||||
|
||||
#[error("Rendering error: {0}")]
|
||||
Rendering(String),
|
||||
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
|
||||
#[error("Wayland protocol error: {0}")]
|
||||
WaylandProtocol(String),
|
||||
|
||||
#[error("Failed to set platform: {0}")]
|
||||
PlatformSetup(String),
|
||||
|
||||
#[error("Failed to flush connection: {0}")]
|
||||
ConnectionFlush(#[from] WaylandError),
|
||||
#[error("rendering operation failed: {message}")]
|
||||
Operation { message: String },
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum EGLError {
|
||||
#[error("failed to create EGL display")]
|
||||
DisplayCreation {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("failed to find EGL configurations")]
|
||||
ConfigSelection {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("no compatible EGL configurations found")]
|
||||
NoCompatibleConfig,
|
||||
|
||||
#[error("failed to create EGL context")]
|
||||
ContextCreation {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("failed to create window surface")]
|
||||
SurfaceCreation {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("failed to make context current")]
|
||||
MakeCurrent {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("failed to swap buffers")]
|
||||
SwapBuffers {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum EventLoopError {
|
||||
#[error("failed to create event loop")]
|
||||
Creation {
|
||||
#[source]
|
||||
source: calloop::Error,
|
||||
},
|
||||
|
||||
#[error("failed to insert event source: {message}")]
|
||||
InsertSource { message: String },
|
||||
|
||||
#[error("event loop execution failed")]
|
||||
Execution {
|
||||
#[source]
|
||||
source: calloop::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LayerShikaError {
|
||||
#[error("domain error")]
|
||||
Domain {
|
||||
#[from]
|
||||
source: DomainError,
|
||||
},
|
||||
|
||||
#[error("failed to connect to Wayland display")]
|
||||
WaylandConnection {
|
||||
#[from]
|
||||
source: ConnectError,
|
||||
},
|
||||
|
||||
#[error("failed to initialize Wayland globals")]
|
||||
GlobalInitialization {
|
||||
#[source]
|
||||
source: GlobalError,
|
||||
},
|
||||
|
||||
#[error("Wayland dispatch error")]
|
||||
WaylandDispatch {
|
||||
#[from]
|
||||
source: DispatchError,
|
||||
},
|
||||
|
||||
#[error("failed to bind Wayland global")]
|
||||
GlobalBind {
|
||||
#[from]
|
||||
source: BindError,
|
||||
},
|
||||
|
||||
#[error("EGL context error")]
|
||||
EGLContext {
|
||||
#[from]
|
||||
source: EGLError,
|
||||
},
|
||||
|
||||
#[error("failed to create FemtoVG renderer")]
|
||||
FemtoVGRendererCreation {
|
||||
#[source]
|
||||
source: PlatformError,
|
||||
},
|
||||
|
||||
#[error("failed to create Slint component")]
|
||||
SlintComponentCreation {
|
||||
#[source]
|
||||
source: PlatformError,
|
||||
},
|
||||
|
||||
#[error("event loop error")]
|
||||
EventLoop {
|
||||
#[from]
|
||||
source: EventLoopError,
|
||||
},
|
||||
|
||||
#[error("window configuration error: {message}")]
|
||||
WindowConfiguration { message: String },
|
||||
|
||||
#[error("rendering error")]
|
||||
Rendering {
|
||||
#[from]
|
||||
source: RenderingError,
|
||||
},
|
||||
|
||||
#[error("invalid input: {message}")]
|
||||
InvalidInput { message: String },
|
||||
|
||||
#[error("Wayland protocol error")]
|
||||
WaylandProtocol {
|
||||
#[source]
|
||||
source: WaylandError,
|
||||
},
|
||||
|
||||
#[error("failed to set up Slint platform")]
|
||||
PlatformSetup {
|
||||
#[source]
|
||||
source: SetPlatformError,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
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 +0,0 @@
|
|||
pub mod calloop_adapter;
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
#![allow(clippy::pub_use)]
|
||||
|
||||
pub mod errors;
|
||||
pub mod event_loop;
|
||||
pub mod rendering;
|
||||
pub mod wayland;
|
||||
|
||||
pub use rendering::femtovg::popup_window::PopupWindow;
|
||||
pub use rendering::slint_integration::platform::close_current_popup;
|
||||
|
||||
pub mod platform {
|
||||
pub use slint;
|
||||
pub use slint_interpreter;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::errors::{LayerShikaError, Result};
|
||||
use crate::errors::{EGLError, LayerShikaError, Result};
|
||||
use glutin::{
|
||||
api::egl::{
|
||||
config::Config,
|
||||
|
|
@ -15,10 +15,10 @@ use glutin::{
|
|||
use raw_window_handle::{
|
||||
RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle,
|
||||
};
|
||||
use slint::{platform::femtovg_renderer::OpenGLInterface, PhysicalSize};
|
||||
use slint::{PhysicalSize, platform::femtovg_renderer::OpenGLInterface};
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::{self, c_void, CStr},
|
||||
ffi::{self, CStr, c_void},
|
||||
num::NonZeroU32,
|
||||
ptr::NonNull,
|
||||
result::Result as StdResult,
|
||||
|
|
@ -63,38 +63,24 @@ impl EGLContextBuilder {
|
|||
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,
|
||||
context_attributes: ContextAttributesBuilder,
|
||||
) -> Self {
|
||||
self.context_attributes = Some(context_attributes);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<EGLContext> {
|
||||
let display_id = self
|
||||
.display_id
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Display ID is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Display ID is required".into(),
|
||||
})?;
|
||||
let surface_id = self
|
||||
.surface_id
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Surface ID is required".into()))?;
|
||||
let size = self
|
||||
.size
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Size is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Surface ID is required".into(),
|
||||
})?;
|
||||
let size = self.size.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Size is required".into(),
|
||||
})?;
|
||||
|
||||
let display_handle = create_wayland_display_handle(&display_id)?;
|
||||
let glutin_display = unsafe { Display::new(display_handle) }.map_err(|e| {
|
||||
LayerShikaError::EGLContextCreation(format!("Failed to create display: {e}"))
|
||||
})?;
|
||||
let glutin_display = unsafe { Display::new(display_handle) }
|
||||
.map_err(|e| EGLError::DisplayCreation { source: e.into() })?;
|
||||
|
||||
let config_template = self.config_template.unwrap_or_default();
|
||||
|
||||
|
|
@ -109,7 +95,7 @@ impl EGLContextBuilder {
|
|||
|
||||
let context = context
|
||||
.make_current(&surface)
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(format!("Unable to activate EGL context: {e}. This may indicate a problem with the graphics drivers.")))?;
|
||||
.map_err(|e| EGLError::MakeCurrent { source: e.into() })?;
|
||||
|
||||
Ok(EGLContext { surface, context })
|
||||
}
|
||||
|
|
@ -123,9 +109,9 @@ impl EGLContext {
|
|||
|
||||
fn ensure_current(&self) -> Result<()> {
|
||||
if !self.context.is_current() {
|
||||
self.context.make_current(&self.surface).map_err(|e| {
|
||||
LayerShikaError::EGLContextCreation(format!("Failed to make context current: {e}"))
|
||||
})?;
|
||||
self.context
|
||||
.make_current(&self.surface)
|
||||
.map_err(|e| EGLError::MakeCurrent { source: e.into() })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -145,7 +131,9 @@ impl Drop for EGLContext {
|
|||
|
||||
fn create_wayland_display_handle(display_id: &ObjectId) -> Result<RawDisplayHandle> {
|
||||
let display = NonNull::new(display_id.as_ptr().cast::<c_void>()).ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput("Failed to create NonNull pointer for display".into())
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "Failed to create NonNull pointer for display".into(),
|
||||
}
|
||||
})?;
|
||||
let handle = WaylandDisplayHandle::new(display);
|
||||
Ok(RawDisplayHandle::Wayland(handle))
|
||||
|
|
@ -156,10 +144,10 @@ fn select_config(
|
|||
config_template: ConfigTemplateBuilder,
|
||||
) -> Result<Config> {
|
||||
let mut configs = unsafe { glutin_display.find_configs(config_template.build()) }
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(format!("Failed to find configs: {e}")))?;
|
||||
configs.next().ok_or_else(|| {
|
||||
LayerShikaError::EGLContextCreation("No compatible EGL configurations found.".into())
|
||||
})
|
||||
.map_err(|e| EGLError::ConfigSelection { source: e.into() })?;
|
||||
configs
|
||||
.next()
|
||||
.ok_or_else(|| EGLError::NoCompatibleConfig.into())
|
||||
}
|
||||
|
||||
fn create_context(
|
||||
|
|
@ -168,12 +156,14 @@ fn create_context(
|
|||
context_attributes: ContextAttributesBuilder,
|
||||
) -> Result<NotCurrentContext> {
|
||||
unsafe { glutin_display.create_context(config, &context_attributes.build(None)) }
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(format!("Failed to create context: {e}")))
|
||||
.map_err(|e| EGLError::ContextCreation { source: e.into() }.into())
|
||||
}
|
||||
|
||||
fn create_surface_handle(surface_id: &ObjectId) -> Result<RawWindowHandle> {
|
||||
let surface = NonNull::new(surface_id.as_ptr().cast::<c_void>()).ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput("Failed to create NonNull pointer for surface".into())
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "Failed to create NonNull pointer for surface".into(),
|
||||
}
|
||||
})?;
|
||||
let handle = WaylandWindowHandle::new(surface);
|
||||
Ok(RawWindowHandle::Wayland(handle))
|
||||
|
|
@ -185,18 +175,19 @@ fn create_surface(
|
|||
surface_handle: RawWindowHandle,
|
||||
size: PhysicalSize,
|
||||
) -> Result<Surface<WindowSurface>> {
|
||||
let width = NonZeroU32::new(size.width)
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Width cannot be zero".into()))?;
|
||||
let width = NonZeroU32::new(size.width).ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Width cannot be zero".into(),
|
||||
})?;
|
||||
|
||||
let height = NonZeroU32::new(size.height)
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Height cannot be zero".into()))?;
|
||||
let height = NonZeroU32::new(size.height).ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Height cannot be zero".into(),
|
||||
})?;
|
||||
|
||||
let attrs =
|
||||
SurfaceAttributesBuilder::<WindowSurface>::new().build(surface_handle, width, height);
|
||||
|
||||
unsafe { glutin_display.create_window_surface(config, &attrs) }.map_err(|e| {
|
||||
LayerShikaError::EGLContextCreation(format!("Failed to create window surface: {e}"))
|
||||
})
|
||||
unsafe { glutin_display.create_window_surface(config, &attrs) }
|
||||
.map_err(|e| EGLError::SurfaceCreation { source: e.into() }.into())
|
||||
}
|
||||
|
||||
unsafe impl OpenGLInterface for EGLContext {
|
||||
|
|
@ -206,9 +197,9 @@ unsafe impl OpenGLInterface for EGLContext {
|
|||
}
|
||||
|
||||
fn swap_buffers(&self) -> StdResult<(), Box<dyn Error + Send + Sync>> {
|
||||
self.surface.swap_buffers(&self.context).map_err(|e| {
|
||||
LayerShikaError::EGLContextCreation(format!("Failed to swap buffers: {e}")).into()
|
||||
})
|
||||
self.surface
|
||||
.swap_buffers(&self.context)
|
||||
.map_err(|e| EGLError::SwapBuffers { source: e.into() }.into())
|
||||
}
|
||||
|
||||
fn resize(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::errors::{LayerShikaError, Result};
|
||||
use crate::errors::{RenderingError, Result};
|
||||
use core::ops::Deref;
|
||||
use log::info;
|
||||
use slint::{
|
||||
platform::{femtovg_renderer::FemtoVGRenderer, Renderer, WindowAdapter, WindowEvent},
|
||||
PhysicalSize, Window, WindowSize,
|
||||
platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer},
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
|
@ -43,7 +43,9 @@ impl FemtoVGWindow {
|
|||
) {
|
||||
self.renderer
|
||||
.render()
|
||||
.map_err(|e| LayerShikaError::Rendering(format!("Error rendering frame: {e}")))?;
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: format!("Error rendering frame: {e}"),
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use crate::errors::{LayerShikaError, Result};
|
||||
use crate::errors::{RenderingError, Result};
|
||||
use crate::wayland::surfaces::popup_manager::PopupManager;
|
||||
use core::ops::Deref;
|
||||
use log::info;
|
||||
use slint::{
|
||||
platform::{femtovg_renderer::FemtoVGRenderer, Renderer, WindowAdapter, WindowEvent},
|
||||
PhysicalSize, Window, WindowSize,
|
||||
platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer},
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use super::main_window::RenderState;
|
||||
|
|
@ -17,6 +18,8 @@ pub struct PopupWindow {
|
|||
render_state: Cell<RenderState>,
|
||||
size: Cell<PhysicalSize>,
|
||||
scale_factor: Cell<f32>,
|
||||
popup_manager: RefCell<Weak<PopupManager>>,
|
||||
popup_key: Cell<Option<usize>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -31,10 +34,37 @@ impl PopupWindow {
|
|||
render_state: Cell::new(RenderState::Clean),
|
||||
size: Cell::new(PhysicalSize::default()),
|
||||
scale_factor: Cell::new(1.),
|
||||
popup_manager: RefCell::new(Weak::new()),
|
||||
popup_key: Cell::new(None),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_popup_manager(&self, popup_manager: Weak<PopupManager>, key: usize) {
|
||||
*self.popup_manager.borrow_mut() = popup_manager;
|
||||
self.popup_key.set(Some(key));
|
||||
}
|
||||
|
||||
pub fn close_popup(&self) {
|
||||
info!("Closing popup window - cleaning up resources");
|
||||
|
||||
if let Err(e) = self.window.hide() {
|
||||
info!("Failed to hide popup window: {e}");
|
||||
}
|
||||
|
||||
if let Some(popup_manager) = self.popup_manager.borrow().upgrade() {
|
||||
if let Some(key) = self.popup_key.get() {
|
||||
info!("Destroying popup with key {key}");
|
||||
popup_manager.destroy_popup(key);
|
||||
}
|
||||
}
|
||||
|
||||
*self.popup_manager.borrow_mut() = Weak::new();
|
||||
self.popup_key.set(None);
|
||||
|
||||
info!("Popup window cleanup complete");
|
||||
}
|
||||
|
||||
pub fn render_frame_if_dirty(&self) -> Result<()> {
|
||||
if matches!(
|
||||
self.render_state.replace(RenderState::Clean),
|
||||
|
|
@ -45,9 +75,11 @@ impl PopupWindow {
|
|||
self.size.get(),
|
||||
self.scale_factor.get()
|
||||
);
|
||||
self.renderer.render().map_err(|e| {
|
||||
LayerShikaError::Rendering(format!("Error rendering popup frame: {e}"))
|
||||
})?;
|
||||
self.renderer
|
||||
.render()
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: format!("Error rendering popup frame: {e}"),
|
||||
})?;
|
||||
info!("Popup frame rendered successfully");
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -96,3 +128,9 @@ impl Deref for PopupWindow {
|
|||
&self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PopupWindow {
|
||||
fn drop(&mut self) {
|
||||
info!("PopupWindow being dropped - resources will be released");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,51 @@
|
|||
use slint::{
|
||||
platform::{Platform, WindowAdapter},
|
||||
PlatformError,
|
||||
platform::{Platform, WindowAdapter},
|
||||
};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
||||
use crate::rendering::femtovg::popup_window::PopupWindow;
|
||||
|
||||
type PopupCreator = dyn Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError>;
|
||||
|
||||
thread_local! {
|
||||
static CURRENT_PLATFORM: RefCell<Option<Weak<CustomSlintPlatform>>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
pub fn close_current_popup() {
|
||||
CURRENT_PLATFORM.with(|platform| {
|
||||
if let Some(weak_platform) = platform.borrow().as_ref() {
|
||||
if let Some(strong_platform) = weak_platform.upgrade() {
|
||||
strong_platform.close_current_popup();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub struct CustomSlintPlatform {
|
||||
main_window: Weak<FemtoVGWindow>,
|
||||
popup_creator: RefCell<Option<Rc<PopupCreator>>>,
|
||||
first_call: Cell<bool>,
|
||||
last_popup: RefCell<Option<Weak<PopupWindow>>>,
|
||||
}
|
||||
|
||||
impl CustomSlintPlatform {
|
||||
#[must_use]
|
||||
pub fn new(window: &Rc<FemtoVGWindow>) -> Self {
|
||||
Self {
|
||||
pub fn new(window: &Rc<FemtoVGWindow>) -> Rc<Self> {
|
||||
let platform = Rc::new(Self {
|
||||
main_window: Rc::downgrade(window),
|
||||
popup_creator: RefCell::new(None),
|
||||
first_call: Cell::new(true),
|
||||
}
|
||||
last_popup: RefCell::new(None),
|
||||
});
|
||||
|
||||
CURRENT_PLATFORM.with(|current| {
|
||||
*current.borrow_mut() = Some(Rc::downgrade(&platform));
|
||||
});
|
||||
|
||||
platform
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -32,6 +55,19 @@ impl CustomSlintPlatform {
|
|||
{
|
||||
*self.popup_creator.borrow_mut() = Some(Rc::new(creator));
|
||||
}
|
||||
|
||||
pub fn set_last_popup(&self, popup: &Rc<PopupWindow>) {
|
||||
*self.last_popup.borrow_mut() = Some(Rc::downgrade(popup));
|
||||
}
|
||||
|
||||
pub fn close_current_popup(&self) {
|
||||
if let Some(weak_popup) = self.last_popup.borrow().as_ref() {
|
||||
if let Some(popup) = weak_popup.upgrade() {
|
||||
popup.close_popup();
|
||||
}
|
||||
}
|
||||
*self.last_popup.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for CustomSlintPlatform {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use layer_shika_domain::config::{AnchorEdges, Layer, Margins, WindowConfig as DomainWindowConfig};
|
||||
use layer_shika_domain::prelude::{
|
||||
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},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use crate::errors::{LayerShikaError, Result};
|
||||
use crate::errors::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 connection = Rc::new(Connection::connect_to_env()?);
|
||||
let event_queue = connection.new_event_queue();
|
||||
Ok((connection, event_queue))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::wayland::surfaces::surface_state::WindowState;
|
||||
use crate::impl_empty_dispatch;
|
||||
use crate::wayland::surfaces::surface_state::WindowState;
|
||||
use log::info;
|
||||
use slint::{
|
||||
platform::{PointerEventButton, WindowEvent},
|
||||
PhysicalSize,
|
||||
platform::{PointerEventButton, WindowEvent},
|
||||
};
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
|
||||
zwlr_layer_shell_v1::ZwlrLayerShellV1,
|
||||
|
|
@ -11,6 +11,7 @@ use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
|
|||
};
|
||||
use wayland_client::WEnum;
|
||||
use wayland_client::{
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
globals::GlobalListContents,
|
||||
protocol::{
|
||||
wl_compositor::WlCompositor,
|
||||
|
|
@ -20,7 +21,6 @@ use wayland_client::{
|
|||
wl_seat::WlSeat,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||
|
|
@ -138,7 +138,9 @@ impl Dispatch<WlOutput, ()> for WindowState {
|
|||
model,
|
||||
transform,
|
||||
} => {
|
||||
info!("WlOutput geometry: x={x}, y={y}, physical_width={physical_width}, physical_height={physical_height}, subpixel={subpixel:?}, make={make:?}, model={model:?}, transform={transform:?}");
|
||||
info!(
|
||||
"WlOutput geometry: x={x}, y={y}, physical_width={physical_width}, physical_height={physical_height}, subpixel={subpixel:?}, make={make:?}, model={model:?}, transform={transform:?}"
|
||||
);
|
||||
}
|
||||
wl_output::Event::Done => {
|
||||
info!("WlOutput done");
|
||||
|
|
@ -270,16 +272,16 @@ impl Dispatch<XdgPopup, ()> for WindowState {
|
|||
xdg_popup::Event::PopupDone => {
|
||||
info!("XdgPopup dismissed by compositor");
|
||||
let popup_id = xdg_popup.id();
|
||||
let popup_index = state
|
||||
let popup_key = state
|
||||
.popup_manager()
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.find_popup_index_by_xdg_popup_id(&popup_id));
|
||||
.and_then(|pm| pm.find_popup_key_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(key) = popup_key {
|
||||
info!("Destroying popup with key {key}");
|
||||
state.clear_active_window_if_popup(key);
|
||||
if let Some(popup_manager) = &state.popup_manager() {
|
||||
popup_manager.destroy_popup(index);
|
||||
popup_manager.destroy_popup(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ macro_rules! bind_globals {
|
|||
($global_list:expr, $queue_handle:expr, $(($interface:ty, $name:ident, $version:expr)),+) => {
|
||||
{
|
||||
$(
|
||||
let $name: $interface = $global_list.bind($queue_handle, $version, ())
|
||||
.map_err(|e| LayerShikaError::WaylandDispatch(e.to_string()))?;
|
||||
let $name: $interface = $global_list.bind($queue_handle, $version, ())?;
|
||||
)+
|
||||
Ok::<($($interface,)+), LayerShikaError>(($($name,)+))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl GlobalContext {
|
|||
) -> Result<Self, LayerShikaError> {
|
||||
let global_list = registry_queue_init::<WindowState>(connection)
|
||||
.map(|(global_list, _)| global_list)
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization { source: e })?;
|
||||
|
||||
let (compositor, output, layer_shell, seat) = bind_globals!(
|
||||
&global_list,
|
||||
|
|
|
|||
|
|
@ -1,29 +1,36 @@
|
|||
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::main_window::FemtoVGWindow, slint_integration::platform::CustomSlintPlatform,
|
||||
surfaces::popup_manager::{PopupContext, PopupManager},
|
||||
surfaces::{
|
||||
surface_builder::WindowStateBuilder,
|
||||
surface_state::{SharedPointerSerial, WindowState},
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
errors::{EventLoopError, LayerShikaError, RenderingError, Result},
|
||||
rendering::{
|
||||
egl::context::EGLContext, femtovg::main_window::FemtoVGWindow,
|
||||
slint_integration::platform::CustomSlintPlatform,
|
||||
},
|
||||
};
|
||||
use core::result::Result as CoreResult;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::ports::windowing::WindowingSystemPort;
|
||||
use log::{error, info};
|
||||
use slint::{
|
||||
platform::{femtovg_renderer::FemtoVGRenderer, update_timers_and_animations, WindowAdapter},
|
||||
LogicalPosition, PhysicalSize, PlatformError, WindowPosition,
|
||||
platform::{WindowAdapter, femtovg_renderer::FemtoVGRenderer, update_timers_and_animations},
|
||||
};
|
||||
use slint_interpreter::ComponentInstance;
|
||||
use smithay_client_toolkit::reexports::calloop::{
|
||||
generic::Generic, EventLoop, Interest, LoopHandle, Mode, PostAction,
|
||||
EventLoop, Interest, LoopHandle, Mode, PostAction, generic::Generic,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use wayland_client::{
|
||||
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
||||
Connection, EventQueue, Proxy,
|
||||
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
||||
};
|
||||
|
||||
pub struct WaylandWindowingSystem {
|
||||
|
|
@ -40,7 +47,7 @@ impl WaylandWindowingSystem {
|
|||
let (connection, event_queue) = Self::init_wayland_connection()?;
|
||||
let (state, global_ctx, platform) = Self::init_state(config, &connection, &event_queue)?;
|
||||
let event_loop =
|
||||
EventLoop::try_new().map_err(|e| LayerShikaError::EventLoop(e.to_string()))?;
|
||||
EventLoop::try_new().map_err(|e| EventLoopError::Creation { source: e })?;
|
||||
|
||||
let popup_context = PopupContext::new(
|
||||
global_ctx.compositor,
|
||||
|
|
@ -53,8 +60,15 @@ impl WaylandWindowingSystem {
|
|||
);
|
||||
|
||||
let popup_manager = Rc::new(PopupManager::new(popup_context, state.scale_factor()));
|
||||
let shared_serial = Rc::new(SharedPointerSerial::new());
|
||||
|
||||
Self::setup_popup_creator(&popup_manager, &platform, &state, &event_queue);
|
||||
Self::setup_popup_creator(
|
||||
&popup_manager,
|
||||
&platform,
|
||||
&state,
|
||||
&event_queue,
|
||||
&shared_serial,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
|
|
@ -67,13 +81,13 @@ impl WaylandWindowingSystem {
|
|||
system
|
||||
.state
|
||||
.set_popup_manager(Rc::clone(&system.popup_manager));
|
||||
system.state.set_shared_pointer_serial(shared_serial);
|
||||
system
|
||||
})
|
||||
}
|
||||
|
||||
fn init_wayland_connection() -> Result<(Rc<Connection>, EventQueue<WindowState>)> {
|
||||
let connection =
|
||||
Rc::new(Connection::connect_to_env().map_err(LayerShikaError::WaylandConnection)?);
|
||||
let connection = Rc::new(Connection::connect_to_env()?);
|
||||
let event_queue = connection.new_event_queue();
|
||||
Ok((connection, event_queue))
|
||||
}
|
||||
|
|
@ -83,8 +97,7 @@ impl WaylandWindowingSystem {
|
|||
connection: &Connection,
|
||||
event_queue: &EventQueue<WindowState>,
|
||||
) -> Result<(WindowState, GlobalContext, Rc<CustomSlintPlatform>)> {
|
||||
let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
||||
let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())?;
|
||||
|
||||
let layer_surface_params = LayerSurfaceParams {
|
||||
anchor: config.anchor,
|
||||
|
|
@ -107,22 +120,20 @@ impl WaylandWindowingSystem {
|
|||
|
||||
let surface_ctx = SurfaceCtx::setup(&setup_params, &layer_surface_params);
|
||||
|
||||
let pointer = Rc::new(global_ctx.seat.get_pointer(&event_queue.handle(), ()));
|
||||
let output = Rc::new(global_ctx.output.clone());
|
||||
let window =
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), &config)
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(e.to_string()))?;
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), &config)?;
|
||||
|
||||
let pointer = Rc::new(global_ctx.seat.get_pointer(&event_queue.handle(), ()));
|
||||
|
||||
let mut builder = WindowStateBuilder::new()
|
||||
.with_component_definition(config.component_definition)
|
||||
.with_surface(Rc::clone(&surface_ctx.surface))
|
||||
.with_layer_surface(Rc::clone(&surface_ctx.layer_surface))
|
||||
.with_pointer(Rc::clone(&pointer))
|
||||
.with_output(Rc::clone(&output))
|
||||
.with_scale_factor(config.scale_factor)
|
||||
.with_height(config.height)
|
||||
.with_exclusive_zone(config.exclusive_zone)
|
||||
.with_connection(Rc::new(connection.clone()))
|
||||
.with_pointer(Rc::clone(&pointer))
|
||||
.with_window(window);
|
||||
|
||||
if let Some(fs) = &surface_ctx.fractional_scale {
|
||||
|
|
@ -133,9 +144,12 @@ impl WaylandWindowingSystem {
|
|||
builder = builder.with_viewport(Rc::clone(vp));
|
||||
}
|
||||
|
||||
let (state, platform) = builder
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::WindowConfiguration(e.to_string()))?;
|
||||
let (state, platform) =
|
||||
builder
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
Ok((state, global_ctx, platform))
|
||||
}
|
||||
|
|
@ -145,6 +159,7 @@ impl WaylandWindowingSystem {
|
|||
platform: &Rc<CustomSlintPlatform>,
|
||||
state: &WindowState,
|
||||
event_queue: &EventQueue<WindowState>,
|
||||
shared_serial: &Rc<SharedPointerSerial>,
|
||||
) {
|
||||
if !popup_manager.has_xdg_shell() {
|
||||
info!("xdg-shell not available, popups will not be supported");
|
||||
|
|
@ -154,16 +169,25 @@ impl WaylandWindowingSystem {
|
|||
info!("Setting up popup creator with xdg-shell support");
|
||||
|
||||
let popup_manager_clone = Rc::clone(popup_manager);
|
||||
let platform_weak = Rc::downgrade(platform);
|
||||
let layer_surface = state.layer_surface();
|
||||
let queue_handle = event_queue.handle();
|
||||
let serial_holder = Rc::clone(shared_serial);
|
||||
|
||||
platform.set_popup_creator(move || {
|
||||
info!("Popup creator called! Creating popup window...");
|
||||
|
||||
let result = popup_manager_clone
|
||||
.create_popup(&queue_handle, &layer_surface, 0)
|
||||
.map(|w| w as Rc<dyn WindowAdapter>)
|
||||
.map_err(|e| PlatformError::Other(format!("Failed to create popup: {e}")));
|
||||
let serial = serial_holder.get();
|
||||
|
||||
let popup_window = popup_manager_clone
|
||||
.create_popup(&queue_handle, &layer_surface, serial)
|
||||
.map_err(|e| PlatformError::Other(format!("Failed to create popup: {e}")))?;
|
||||
|
||||
if let Some(platform) = platform_weak.upgrade() {
|
||||
platform.set_last_popup(&popup_window);
|
||||
}
|
||||
|
||||
let result = Ok(popup_window as Rc<dyn WindowAdapter>);
|
||||
|
||||
match &result {
|
||||
Ok(_) => info!("Popup created successfully"),
|
||||
|
|
@ -185,11 +209,10 @@ impl WaylandWindowingSystem {
|
|||
.with_display_id(display.id())
|
||||
.with_surface_id(surface.id())
|
||||
.with_size(init_size)
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(e.to_string()))?;
|
||||
.build()?;
|
||||
|
||||
let renderer = FemtoVGRenderer::new(context)
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
||||
|
||||
let femtovg_window = FemtoVGWindow::new(renderer);
|
||||
femtovg_window.set_size(slint::WindowSize::Physical(init_size));
|
||||
|
|
@ -207,21 +230,18 @@ impl WaylandWindowingSystem {
|
|||
info!("Starting WindowingSystem main loop");
|
||||
|
||||
info!("Processing initial Wayland configuration events");
|
||||
while self
|
||||
.event_queue
|
||||
.blocking_dispatch(&mut self.state)
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?
|
||||
> 0
|
||||
{
|
||||
while self.event_queue.blocking_dispatch(&mut self.state)? > 0 {
|
||||
self.connection
|
||||
.flush()
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol { source: e })?;
|
||||
|
||||
update_timers_and_animations();
|
||||
self.state
|
||||
.window()
|
||||
.render_frame_if_dirty()
|
||||
.map_err(|e| LayerShikaError::Rendering(e.to_string()))?;
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
}
|
||||
|
||||
self.setup_wayland_event_source()?;
|
||||
|
|
@ -238,7 +258,9 @@ impl WaylandWindowingSystem {
|
|||
error!("Error processing events: {e}");
|
||||
}
|
||||
})
|
||||
.map_err(|e| LayerShikaError::EventLoop(e.to_string()))
|
||||
.map_err(|e| EventLoopError::Execution { source: e })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_wayland_event_source(&self) -> Result<()> {
|
||||
|
|
@ -250,7 +272,9 @@ impl WaylandWindowingSystem {
|
|||
Generic::new(connection, Interest::READ, Mode::Level),
|
||||
move |_, _connection, _shared_data| Ok(PostAction::Continue),
|
||||
)
|
||||
.map_err(|e| LayerShikaError::EventLoop(e.to_string()))?;
|
||||
.map_err(|e| EventLoopError::InsertSource {
|
||||
message: format!("{e:?}"),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -264,27 +288,29 @@ impl WaylandWindowingSystem {
|
|||
if let Some(guard) = event_queue.prepare_read() {
|
||||
guard
|
||||
.read()
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol { source: e })?;
|
||||
}
|
||||
|
||||
event_queue
|
||||
.dispatch_pending(shared_data)
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
||||
event_queue.dispatch_pending(shared_data)?;
|
||||
|
||||
update_timers_and_animations();
|
||||
|
||||
shared_data
|
||||
.window()
|
||||
.render_frame_if_dirty()
|
||||
.map_err(|e| LayerShikaError::Rendering(e.to_string()))?;
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
popup_manager
|
||||
.render_popups()
|
||||
.map_err(|e| LayerShikaError::Rendering(e.to_string()))?;
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
connection
|
||||
.flush()
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::WaylandProtocol { source: e })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -293,12 +319,15 @@ impl WaylandWindowingSystem {
|
|||
self.state.component_instance()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
|
||||
self.state.window()
|
||||
}
|
||||
|
||||
pub const fn state(&self) -> &WindowState {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowingSystemPort for WaylandWindowingSystem {
|
||||
fn run(&mut self) -> CoreResult<(), DomainError> {
|
||||
WaylandWindowingSystem::run(self).map_err(|e| DomainError::Adapter {
|
||||
source: Box::new(e),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
|
|||
};
|
||||
use std::rc::Rc;
|
||||
use wayland_client::{
|
||||
protocol::{wl_compositor::WlCompositor, wl_output::WlOutput, wl_surface::WlSurface},
|
||||
QueueHandle,
|
||||
protocol::{wl_compositor::WlCompositor, wl_output::WlOutput, wl_surface::WlSurface},
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::errors::{LayerShikaError, Result};
|
|||
use crate::rendering::egl::context::EGLContext;
|
||||
use crate::rendering::femtovg::popup_window::PopupWindow;
|
||||
use log::info;
|
||||
use slab::Slab;
|
||||
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 std::cell::RefCell;
|
||||
|
|
@ -25,20 +26,18 @@ pub struct PopupContext {
|
|||
fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
|
||||
viewporter: Option<WpViewporter>,
|
||||
display: WlDisplay,
|
||||
#[allow(dead_code)]
|
||||
connection: Rc<Connection>,
|
||||
}
|
||||
|
||||
impl PopupContext {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
pub fn new(
|
||||
compositor: WlCompositor,
|
||||
xdg_wm_base: Option<XdgWmBase>,
|
||||
seat: WlSeat,
|
||||
fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
|
||||
viewporter: Option<WpViewporter>,
|
||||
display: WlDisplay,
|
||||
connection: Rc<Connection>,
|
||||
_connection: Rc<Connection>,
|
||||
) -> Self {
|
||||
Self {
|
||||
compositor,
|
||||
|
|
@ -47,7 +46,6 @@ impl PopupContext {
|
|||
fractional_scale_manager,
|
||||
viewporter,
|
||||
display,
|
||||
connection,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +57,7 @@ struct ActivePopup {
|
|||
|
||||
pub struct PopupManager {
|
||||
context: PopupContext,
|
||||
popups: RefCell<Vec<ActivePopup>>,
|
||||
popups: RefCell<Slab<ActivePopup>>,
|
||||
current_scale_factor: RefCell<f32>,
|
||||
current_output_size: RefCell<PhysicalSize>,
|
||||
}
|
||||
|
|
@ -69,7 +67,7 @@ impl PopupManager {
|
|||
pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self {
|
||||
Self {
|
||||
context,
|
||||
popups: RefCell::new(Vec::new()),
|
||||
popups: RefCell::new(Slab::new()),
|
||||
current_scale_factor: RefCell::new(initial_scale_factor),
|
||||
current_output_size: RefCell::new(PhysicalSize::new(0, 0)),
|
||||
}
|
||||
|
|
@ -84,18 +82,22 @@ impl PopupManager {
|
|||
}
|
||||
|
||||
pub fn create_popup(
|
||||
&self,
|
||||
self: &Rc<Self>,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
parent_layer_surface: &ZwlrLayerSurfaceV1,
|
||||
last_pointer_serial: u32,
|
||||
) -> Result<Rc<PopupWindow>> {
|
||||
let xdg_wm_base = self.context.xdg_wm_base.as_ref().ok_or_else(|| {
|
||||
LayerShikaError::WaylandProtocol("xdg-shell not available for popups".into())
|
||||
LayerShikaError::WindowConfiguration {
|
||||
message: "xdg-shell not available for popups".into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let scale_factor = *self.current_scale_factor.borrow();
|
||||
let output_size = *self.current_output_size.borrow();
|
||||
info!("Creating popup window with scale factor {scale_factor} and output size {output_size:?}");
|
||||
info!(
|
||||
"Creating popup window with scale factor {scale_factor} and output size {output_size:?}"
|
||||
);
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let logical_size = slint::LogicalSize::new(
|
||||
|
|
@ -129,28 +131,28 @@ impl PopupManager {
|
|||
.with_display_id(self.context.display.id())
|
||||
.with_surface_id(popup_surface.surface.id())
|
||||
.with_size(popup_size)
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::EGLContextCreation(e.to_string()))?;
|
||||
.build()?;
|
||||
|
||||
let renderer = FemtoVGRenderer::new(context)
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::FemtoVGRendererCreation { source: e })?;
|
||||
|
||||
let popup_window = PopupWindow::new(renderer);
|
||||
popup_window.set_scale_factor(scale_factor);
|
||||
popup_window.set_size(WindowSize::Logical(logical_size));
|
||||
|
||||
info!("Popup window created successfully");
|
||||
|
||||
self.popups.borrow_mut().push(ActivePopup {
|
||||
let key = self.popups.borrow_mut().insert(ActivePopup {
|
||||
surface: popup_surface,
|
||||
window: Rc::clone(&popup_window),
|
||||
});
|
||||
popup_window.set_popup_manager(Rc::downgrade(self), key);
|
||||
|
||||
info!("Popup window created successfully with key {key}");
|
||||
|
||||
Ok(popup_window)
|
||||
}
|
||||
|
||||
pub fn render_popups(&self) -> Result<()> {
|
||||
for popup in self.popups.borrow().iter() {
|
||||
for (_key, popup) in self.popups.borrow().iter() {
|
||||
popup.window.render_frame_if_dirty()?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -160,61 +162,51 @@ impl PopupManager {
|
|||
self.context.xdg_wm_base.is_some()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn popup_count(&self) -> usize {
|
||||
self.popups.borrow().len()
|
||||
}
|
||||
|
||||
pub fn mark_all_popups_dirty(&self) {
|
||||
for popup in self.popups.borrow().iter() {
|
||||
for (_key, popup) in self.popups.borrow().iter() {
|
||||
popup.window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_popup_index_by_surface_id(&self, surface_id: &ObjectId) -> Option<usize> {
|
||||
for (index, popup) in self.popups.borrow().iter().enumerate() {
|
||||
if popup.surface.surface.id() == *surface_id {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn find_popup_key_by_surface_id(&self, surface_id: &ObjectId) -> Option<usize> {
|
||||
self.popups
|
||||
.borrow()
|
||||
.iter()
|
||||
.find_map(|(key, popup)| (popup.surface.surface.id() == *surface_id).then_some(key))
|
||||
}
|
||||
|
||||
pub fn find_popup_index_by_fractional_scale_id(
|
||||
pub fn find_popup_key_by_fractional_scale_id(
|
||||
&self,
|
||||
fractional_scale_id: &ObjectId,
|
||||
) -> Option<usize> {
|
||||
for (index, popup) in self.popups.borrow().iter().enumerate() {
|
||||
if let Some(ref fs) = popup.surface.fractional_scale {
|
||||
if fs.id() == *fractional_scale_id {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
self.popups.borrow().iter().find_map(|(key, popup)| {
|
||||
popup
|
||||
.surface
|
||||
.fractional_scale
|
||||
.as_ref()
|
||||
.filter(|fs| fs.id() == *fractional_scale_id)
|
||||
.map(|_| key)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_popup_window(&self, index: usize) -> Option<Rc<PopupWindow>> {
|
||||
pub fn get_popup_window(&self, key: usize) -> Option<Rc<PopupWindow>> {
|
||||
self.popups
|
||||
.borrow()
|
||||
.get(index)
|
||||
.get(key)
|
||||
.map(|popup| Rc::clone(&popup.window))
|
||||
}
|
||||
|
||||
pub fn destroy_popup(&self, index: usize) {
|
||||
let mut popups = self.popups.borrow_mut();
|
||||
if index < popups.len() {
|
||||
info!("Destroying popup at index {index}");
|
||||
popups.remove(index);
|
||||
pub fn destroy_popup(&self, key: usize) {
|
||||
if let Some(popup) = self.popups.borrow_mut().try_remove(key) {
|
||||
info!("Destroying popup with key {key}");
|
||||
popup.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_popup_index_by_xdg_popup_id(&self, xdg_popup_id: &ObjectId) -> Option<usize> {
|
||||
for (index, popup) in self.popups.borrow().iter().enumerate() {
|
||||
if popup.surface.xdg_popup.id() == *xdg_popup_id {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn find_popup_key_by_xdg_popup_id(&self, xdg_popup_id: &ObjectId) -> Option<usize> {
|
||||
self.popups
|
||||
.borrow()
|
||||
.iter()
|
||||
.find_map(|(key, popup)| (popup.surface.xdg_popup.id() == *xdg_popup_id).then_some(key))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,4 +133,11 @@ impl PopupSurface {
|
|||
info!("Grabbing popup with serial {serial}");
|
||||
self.xdg_popup.grab(seat, serial);
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
info!("Destroying popup surface");
|
||||
self.xdg_popup.destroy();
|
||||
self.xdg_surface.destroy();
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use slint::{
|
|||
};
|
||||
use slint_interpreter::ComponentDefinition;
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||
use wayland_client::{protocol::{wl_output::WlOutput, wl_pointer::WlPointer, wl_surface::WlSurface}, Connection};
|
||||
use wayland_client::{protocol::{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};
|
||||
|
|
@ -32,7 +32,6 @@ pub struct WindowStateBuilder {
|
|||
pub size: Option<PhysicalSize>,
|
||||
pub output_size: Option<PhysicalSize>,
|
||||
pub pointer: Option<Rc<WlPointer>>,
|
||||
pub output: Option<Rc<WlOutput>>,
|
||||
pub window: Option<Rc<FemtoVGWindow>>,
|
||||
pub connection: Option<Rc<Connection>>,
|
||||
pub scale_factor: f32,
|
||||
|
|
@ -76,12 +75,6 @@ impl WindowStateBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_output(mut self, output: Rc<WlOutput>) -> Self {
|
||||
self.output = Some(output);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_window(mut self, window: Rc<FemtoVGWindow>) -> Self {
|
||||
self.window = Some(window);
|
||||
|
|
@ -131,13 +124,13 @@ impl WindowStateBuilder {
|
|||
}
|
||||
|
||||
pub fn build(self) -> Result<(WindowState, Rc<CustomSlintPlatform>)> {
|
||||
let platform =
|
||||
Rc::new(CustomSlintPlatform::new(self.window.as_ref().ok_or_else(
|
||||
|| LayerShikaError::InvalidInput("Window is required".into()),
|
||||
)?));
|
||||
set_platform(Box::new(PlatformWrapper(Rc::clone(&platform)))).map_err(|e| {
|
||||
LayerShikaError::PlatformSetup(format!("Failed to set platform: {e:?}"))
|
||||
})?;
|
||||
let platform = CustomSlintPlatform::new(self.window.as_ref().ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput {
|
||||
message: "Window is required".into(),
|
||||
}
|
||||
})?);
|
||||
set_platform(Box::new(PlatformWrapper(Rc::clone(&platform))))
|
||||
.map_err(|e| LayerShikaError::PlatformSetup { source: e })?;
|
||||
|
||||
let state = WindowState::new(self)?;
|
||||
Ok((state, platform))
|
||||
|
|
@ -155,7 +148,6 @@ impl Default for WindowStateBuilder {
|
|||
size: None,
|
||||
output_size: None,
|
||||
pointer: None,
|
||||
output: None,
|
||||
window: None,
|
||||
connection: None,
|
||||
scale_factor: 1.0,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use super::surface_builder::WindowStateBuilder;
|
||||
use super::dimensions::SurfaceDimensionsExt;
|
||||
use super::popup_manager::PopupManager;
|
||||
|
|
@ -8,16 +9,45 @@ use crate::wayland::managed_proxies::{
|
|||
};
|
||||
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
||||
use crate::errors::{LayerShikaError, Result};
|
||||
use core::result::Result as CoreResult;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::ports::windowing::RuntimeStatePort;
|
||||
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
||||
use log::info;
|
||||
use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
|
||||
use slint::platform::{WindowAdapter, WindowEvent};
|
||||
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_client::{protocol::wl_surface::WlSurface, Proxy};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SharedPointerSerial {
|
||||
serial: RefCell<u32>,
|
||||
}
|
||||
|
||||
impl Default for SharedPointerSerial {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedPointerSerial {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
serial: RefCell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&self, serial: u32) {
|
||||
*self.serial.borrow_mut() = serial;
|
||||
}
|
||||
|
||||
pub fn get(&self) -> u32 {
|
||||
*self.serial.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum ScalingMode {
|
||||
FractionalWithViewport,
|
||||
FractionalOnly,
|
||||
|
|
@ -38,14 +68,13 @@ pub struct WindowState {
|
|||
surface: ManagedWlSurface,
|
||||
#[allow(dead_code)]
|
||||
pointer: ManagedWlPointer,
|
||||
#[allow(dead_code)]
|
||||
output: Rc<WlOutput>,
|
||||
size: PhysicalSize,
|
||||
logical_size: PhysicalSize,
|
||||
output_size: PhysicalSize,
|
||||
window: Rc<FemtoVGWindow>,
|
||||
current_pointer_position: LogicalPosition,
|
||||
last_pointer_serial: u32,
|
||||
shared_pointer_serial: Option<Rc<SharedPointerSerial>>,
|
||||
scale_factor: f32,
|
||||
height: u32,
|
||||
exclusive_zone: i32,
|
||||
|
|
@ -55,34 +84,48 @@ pub struct WindowState {
|
|||
|
||||
impl WindowState {
|
||||
pub fn new(builder: WindowStateBuilder) -> Result<Self> {
|
||||
let component_definition = builder.component_definition.ok_or_else(|| {
|
||||
LayerShikaError::InvalidInput("Component definition is required".into())
|
||||
})?;
|
||||
let component_definition =
|
||||
builder
|
||||
.component_definition
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Component definition is required".into(),
|
||||
})?;
|
||||
let window = builder
|
||||
.window
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Window is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Window is required".into(),
|
||||
})?;
|
||||
let component_instance = component_definition
|
||||
.create()
|
||||
.map_err(|e| LayerShikaError::SlintComponentCreation(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::SlintComponentCreation { source: e })?;
|
||||
component_instance
|
||||
.show()
|
||||
.map_err(|e| LayerShikaError::SlintComponentCreation(e.to_string()))?;
|
||||
.map_err(|e| LayerShikaError::SlintComponentCreation { source: e })?;
|
||||
|
||||
window.request_redraw();
|
||||
|
||||
let connection = builder
|
||||
.connection
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Connection is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Connection is required".into(),
|
||||
})?;
|
||||
|
||||
let surface_rc = builder
|
||||
.surface
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Surface is required".into()))?;
|
||||
let layer_surface_rc = builder
|
||||
.layer_surface
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Layer surface is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Surface is required".into(),
|
||||
})?;
|
||||
let layer_surface_rc =
|
||||
builder
|
||||
.layer_surface
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Layer surface is required".into(),
|
||||
})?;
|
||||
let pointer_rc = builder
|
||||
.pointer
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Pointer is required".into()))?;
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "Pointer is required".into(),
|
||||
})?;
|
||||
|
||||
let viewport = builder
|
||||
.viewport
|
||||
|
|
@ -102,15 +145,13 @@ impl WindowState {
|
|||
layer_surface,
|
||||
surface,
|
||||
pointer,
|
||||
output: builder
|
||||
.output
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Output is required".into()))?,
|
||||
size: builder.size.unwrap_or_default(),
|
||||
logical_size: PhysicalSize::default(),
|
||||
output_size: builder.output_size.unwrap_or_default(),
|
||||
window,
|
||||
current_pointer_position: LogicalPosition::default(),
|
||||
last_pointer_serial: 0,
|
||||
shared_pointer_serial: None,
|
||||
scale_factor: builder.scale_factor,
|
||||
height: builder.height,
|
||||
exclusive_zone: builder.exclusive_zone,
|
||||
|
|
@ -130,7 +171,7 @@ impl WindowState {
|
|||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
fn configure_slint_window(&self, dimensions: &SurfaceDimensions, mode: &ScalingMode) {
|
||||
fn configure_slint_window(&self, dimensions: &SurfaceDimensions, mode: ScalingMode) {
|
||||
match mode {
|
||||
ScalingMode::FractionalWithViewport => {
|
||||
self.window
|
||||
|
|
@ -157,7 +198,7 @@ impl WindowState {
|
|||
}
|
||||
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
fn configure_wayland_surface(&self, dimensions: &SurfaceDimensions, mode: &ScalingMode) {
|
||||
fn configure_wayland_surface(&self, dimensions: &SurfaceDimensions, mode: ScalingMode) {
|
||||
match mode {
|
||||
ScalingMode::FractionalWithViewport => {
|
||||
self.surface.set_buffer_scale(1);
|
||||
|
|
@ -199,8 +240,8 @@ impl WindowState {
|
|||
scaling_mode
|
||||
);
|
||||
|
||||
self.configure_slint_window(&dimensions, &scaling_mode);
|
||||
self.configure_wayland_surface(&dimensions, &scaling_mode);
|
||||
self.configure_slint_window(&dimensions, scaling_mode);
|
||||
self.configure_wayland_surface(&dimensions, scaling_mode);
|
||||
|
||||
info!("Window physical size: {:?}", self.window.size());
|
||||
|
||||
|
|
@ -289,8 +330,15 @@ impl WindowState {
|
|||
self.last_pointer_serial
|
||||
}
|
||||
|
||||
pub const fn set_last_pointer_serial(&mut self, serial: u32) {
|
||||
pub fn set_last_pointer_serial(&mut self, serial: u32) {
|
||||
self.last_pointer_serial = serial;
|
||||
if let Some(ref shared_serial) = self.shared_pointer_serial {
|
||||
shared_serial.update(serial);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_shared_pointer_serial(&mut self, shared_serial: Rc<SharedPointerSerial>) {
|
||||
self.shared_pointer_serial = Some(shared_serial);
|
||||
}
|
||||
|
||||
pub fn set_popup_manager(&mut self, popup_manager: Rc<PopupManager>) {
|
||||
|
|
@ -306,8 +354,8 @@ impl WindowState {
|
|||
}
|
||||
|
||||
if let Some(popup_manager) = &self.popup_manager {
|
||||
if let Some(popup_index) = popup_manager.find_popup_index_by_surface_id(&surface_id) {
|
||||
self.active_window = Some(ActiveWindow::Popup(popup_index));
|
||||
if let Some(popup_key) = popup_manager.find_popup_key_by_surface_id(&surface_id) {
|
||||
self.active_window = Some(ActiveWindow::Popup(popup_key));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -347,10 +395,10 @@ impl WindowState {
|
|||
}
|
||||
|
||||
if let Some(popup_manager) = &self.popup_manager {
|
||||
if let Some(popup_index) =
|
||||
popup_manager.find_popup_index_by_fractional_scale_id(&fractional_scale_id)
|
||||
if let Some(popup_key) =
|
||||
popup_manager.find_popup_key_by_fractional_scale_id(&fractional_scale_id)
|
||||
{
|
||||
if let Some(popup_window) = popup_manager.get_popup_window(popup_index) {
|
||||
if let Some(popup_window) = popup_manager.get_popup_window(popup_key) {
|
||||
let new_scale_factor = scale_120ths as f32 / 120.0;
|
||||
info!("Updating popup scale factor to {new_scale_factor} ({scale_120ths}x)");
|
||||
popup_window.set_scale_factor(new_scale_factor);
|
||||
|
|
@ -364,8 +412,8 @@ impl WindowState {
|
|||
self.active_window = None;
|
||||
}
|
||||
|
||||
pub fn clear_active_window_if_popup(&mut self, popup_index: usize) {
|
||||
if self.active_window == Some(ActiveWindow::Popup(popup_index)) {
|
||||
pub fn clear_active_window_if_popup(&mut self, popup_key: usize) {
|
||||
if self.active_window == Some(ActiveWindow::Popup(popup_key)) {
|
||||
self.active_window = None;
|
||||
}
|
||||
}
|
||||
|
|
@ -374,3 +422,11 @@ impl WindowState {
|
|||
&self.popup_manager
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeStatePort for WindowState {
|
||||
fn render_frame_if_dirty(&self) -> CoreResult<(), DomainError> {
|
||||
WindowState::render_frame_if_dirty(self).map_err(|e| DomainError::Adapter {
|
||||
source: Box::new(e),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::system::WindowingSystem;
|
||||
use crate::Result;
|
||||
use crate::system::WindowingSystem;
|
||||
use layer_shika_adapters::platform::slint_interpreter::ComponentDefinition;
|
||||
use layer_shika_domain::config::{AnchorEdges, Layer, Margins, WindowConfig};
|
||||
use layer_shika_domain::prelude::{AnchorEdges, Layer, Margins, WindowConfig};
|
||||
|
||||
pub struct NeedsComponent;
|
||||
pub struct HasComponent {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ use layer_shika_domain::errors::DomainError;
|
|||
use std::result::Result as StdResult;
|
||||
|
||||
pub use builder::LayerShika;
|
||||
pub use layer_shika_adapters::PopupWindow;
|
||||
pub use layer_shika_adapters::close_current_popup;
|
||||
pub use layer_shika_adapters::platform::{calloop, slint, slint_interpreter};
|
||||
pub use layer_shika_domain::config::AnchorEdges;
|
||||
pub use layer_shika_domain::value_objects::anchor::AnchorEdges;
|
||||
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
|
||||
|
|
@ -20,4 +22,10 @@ pub enum Error {
|
|||
|
||||
#[error("Domain error: {0}")]
|
||||
Domain(#[from] DomainError),
|
||||
|
||||
#[error("WindowingSystem has been dropped")]
|
||||
SystemDropped,
|
||||
|
||||
#[error("Cannot run while EventLoopHandle exists")]
|
||||
EventLoopHandleExists,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
use crate::Result;
|
||||
use layer_shika_adapters::platform::calloop::{EventSource, InsertError, RegistrationToken};
|
||||
use crate::{Error, Result};
|
||||
use layer_shika_adapters::errors::EventLoopError;
|
||||
use layer_shika_adapters::platform::calloop::{EventSource, 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,
|
||||
config::WaylandWindowConfig, shell_adapter::WaylandWindowingSystem,
|
||||
surfaces::surface_state::WindowState,
|
||||
};
|
||||
use layer_shika_domain::config::WindowConfig;
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
pub struct EventLoopHandle {
|
||||
adapter: EventLoopAdapter,
|
||||
system: Weak<RefCell<WaylandWindowingSystem>>,
|
||||
}
|
||||
|
||||
impl EventLoopHandle {
|
||||
|
|
@ -20,36 +20,47 @@ impl EventLoopHandle {
|
|||
&self,
|
||||
source: S,
|
||||
mut callback: F,
|
||||
) -> StdResult<RegistrationToken, InsertError<S>>
|
||||
) -> StdResult<RegistrationToken, Error>
|
||||
where
|
||||
S: EventSource<Ret = R> + 'static,
|
||||
F: FnMut(S::Event, &mut S::Metadata, &mut RuntimeState) -> R + 'static,
|
||||
F: FnMut(S::Event, &mut S::Metadata, 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)
|
||||
let system = self.system.upgrade().ok_or(Error::SystemDropped)?;
|
||||
let loop_handle = system.borrow().event_loop_handle();
|
||||
|
||||
loop_handle
|
||||
.insert_source(source, move |event, metadata, window_state| {
|
||||
let runtime_state = RuntimeState { window_state };
|
||||
callback(event, metadata, runtime_state)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Adapter(
|
||||
EventLoopError::InsertSource {
|
||||
message: format!("{e:?}"),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RuntimeState {
|
||||
adapter: RuntimeStateAdapter,
|
||||
pub struct RuntimeState<'a> {
|
||||
window_state: &'a mut WindowState,
|
||||
}
|
||||
|
||||
impl RuntimeState {
|
||||
impl RuntimeState<'_> {
|
||||
#[must_use]
|
||||
pub fn component_instance(&self) -> &ComponentInstance {
|
||||
self.adapter.component_instance()
|
||||
self.window_state.component_instance()
|
||||
}
|
||||
|
||||
pub fn render_frame_if_dirty(&self) -> Result<()> {
|
||||
Ok(self.adapter.render_frame_if_dirty()?)
|
||||
pub fn render_frame_if_dirty(&mut self) -> Result<()> {
|
||||
Ok(self.window_state.render_frame_if_dirty()?)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowingSystem {
|
||||
adapter: SystemAdapter,
|
||||
inner: Rc<RefCell<WaylandWindowingSystem>>,
|
||||
}
|
||||
|
||||
impl WindowingSystem {
|
||||
|
|
@ -58,26 +69,27 @@ impl WindowingSystem {
|
|||
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);
|
||||
let inner = WaylandWindowingSystem::new(wayland_config)?;
|
||||
|
||||
Ok(Self { adapter })
|
||||
Ok(Self {
|
||||
inner: Rc::new(RefCell::new(inner)),
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn event_loop_handle(&self) -> EventLoopHandle {
|
||||
EventLoopHandle {
|
||||
adapter: self.adapter.event_loop_handle(),
|
||||
system: Rc::downgrade(&self.inner),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
self.adapter.run()?;
|
||||
self.inner.borrow_mut().run()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn component_instance(&self) -> &ComponentInstance {
|
||||
self.adapter.component_instance()
|
||||
pub fn component_instance(&self) -> Ref<'_, ComponentInstance> {
|
||||
Ref::map(self.inner.borrow(), |system| system.component_instance())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
#![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;
|
||||
use crate::value_objects::anchor::AnchorEdges;
|
||||
use crate::value_objects::layer::Layer;
|
||||
use crate::value_objects::margins::Margins;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowConfig {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
use std::error::Error;
|
||||
use std::result::Result as StdResult;
|
||||
use thiserror::Error;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
pub type Result<T> = StdResult<T, DomainError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[derive(ThisError, Debug)]
|
||||
pub enum DomainError {
|
||||
#[error("Configuration error: {0}")]
|
||||
Configuration(String),
|
||||
#[error("invalid configuration: {message}")]
|
||||
Configuration { message: String },
|
||||
|
||||
#[error("Invalid dimensions: {0}")]
|
||||
InvalidDimensions(String),
|
||||
#[error("invalid dimensions {width}x{height}")]
|
||||
InvalidDimensions { width: u32, height: u32 },
|
||||
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("invalid input: {message}")]
|
||||
InvalidInput { message: String },
|
||||
|
||||
#[error("Calculation error: {0}")]
|
||||
Calculation(String),
|
||||
#[error("calculation error: {operation} failed - {reason}")]
|
||||
Calculation { operation: String, reason: String },
|
||||
|
||||
#[error("adapter error")]
|
||||
Adapter {
|
||||
#[source]
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
pub mod window_events;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
#[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 },
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod config;
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod events;
|
||||
pub mod ports;
|
||||
pub mod prelude;
|
||||
pub mod surface_dimensions;
|
||||
pub mod value_objects;
|
||||
|
|
|
|||
1
domain/src/ports/mod.rs
Normal file
1
domain/src/ports/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod windowing;
|
||||
9
domain/src/ports/windowing.rs
Normal file
9
domain/src/ports/windowing.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use crate::errors::DomainError;
|
||||
|
||||
pub trait WindowingSystemPort {
|
||||
fn run(&mut self) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
pub trait RuntimeStatePort {
|
||||
fn render_frame_if_dirty(&self) -> Result<(), DomainError>;
|
||||
}
|
||||
12
domain/src/prelude.rs
Normal file
12
domain/src/prelude.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#![allow(clippy::pub_use)]
|
||||
|
||||
pub use crate::config::WindowConfig;
|
||||
pub use crate::entities::component::UiComponentHandle;
|
||||
pub use crate::entities::surface::SurfaceHandle;
|
||||
pub use crate::entities::window::WindowHandle;
|
||||
pub use crate::errors::{DomainError, Result};
|
||||
pub use crate::surface_dimensions::SurfaceDimensions;
|
||||
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;
|
||||
Loading…
Add table
Reference in a new issue