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