mirror of
				https://codeberg.org/waydeer/layer-shika.git
				synced 2025-11-04 03:04:23 +00:00 
			
		
		
		
	feat: add init project draft
This commit is contained in:
		
							parent
							
								
									4697a83f18
								
							
						
					
					
						commit
						155d4071a5
					
				
					 11 changed files with 1215 additions and 0 deletions
				
			
		
							
								
								
									
										24
									
								
								src/common.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/common.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					use slint::PhysicalSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					pub struct LayerSize {
 | 
				
			||||||
 | 
					    size: PhysicalSize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl LayerSize {
 | 
				
			||||||
 | 
					    pub fn new(width: u32, height: u32) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            size: PhysicalSize::new(width, height),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn physical_size(&self) -> PhysicalSize {
 | 
				
			||||||
 | 
					        self.size
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for LayerSize {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::new(1, 1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					mod common;
 | 
				
			||||||
 | 
					mod rendering;
 | 
				
			||||||
 | 
					mod windowing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use windowing::WindowingSystemBuilder;
 | 
				
			||||||
							
								
								
									
										183
									
								
								src/rendering/egl_context.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/rendering/egl_context.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,183 @@
 | 
				
			||||||
 | 
					use anyhow::{anyhow, Result};
 | 
				
			||||||
 | 
					use glutin::{
 | 
				
			||||||
 | 
					    api::egl::{context::PossiblyCurrentContext, display::Display, surface::Surface},
 | 
				
			||||||
 | 
					    config::ConfigTemplateBuilder,
 | 
				
			||||||
 | 
					    context::ContextAttributesBuilder,
 | 
				
			||||||
 | 
					    display::GetGlDisplay,
 | 
				
			||||||
 | 
					    prelude::*,
 | 
				
			||||||
 | 
					    surface::{SurfaceAttributesBuilder, WindowSurface},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use raw_window_handle::{
 | 
				
			||||||
 | 
					    RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use slint::platform::femtovg_renderer::OpenGLInterface;
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    ffi::{self, c_void, CStr},
 | 
				
			||||||
 | 
					    num::NonZeroU32,
 | 
				
			||||||
 | 
					    ptr::NonNull,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use wayland_client::backend::ObjectId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::common::LayerSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct EGLContext {
 | 
				
			||||||
 | 
					    context: PossiblyCurrentContext,
 | 
				
			||||||
 | 
					    surface: Surface<WindowSurface>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct EGLContextBuilder {
 | 
				
			||||||
 | 
					    display_id: Option<ObjectId>,
 | 
				
			||||||
 | 
					    surface_id: Option<ObjectId>,
 | 
				
			||||||
 | 
					    size: Option<LayerSize>,
 | 
				
			||||||
 | 
					    config_template: Option<ConfigTemplateBuilder>,
 | 
				
			||||||
 | 
					    context_attributes: Option<ContextAttributesBuilder>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					impl EGLContextBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Default::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_display_id(mut self, display_id: ObjectId) -> Self {
 | 
				
			||||||
 | 
					        self.display_id = Some(display_id);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_surface_id(mut self, surface_id: ObjectId) -> Self {
 | 
				
			||||||
 | 
					        self.surface_id = Some(surface_id);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_size(mut self, size: LayerSize) -> Self {
 | 
				
			||||||
 | 
					        self.size = Some(size);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_config_template(mut self, config_template: ConfigTemplateBuilder) -> Self {
 | 
				
			||||||
 | 
					        self.config_template = Some(config_template);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub 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(|| anyhow!("Display ID is required"))?;
 | 
				
			||||||
 | 
					        let surface_id = self
 | 
				
			||||||
 | 
					            .surface_id
 | 
				
			||||||
 | 
					            .ok_or_else(|| anyhow!("Surface ID is required"))?;
 | 
				
			||||||
 | 
					        let size = self.size.ok_or_else(|| anyhow!("Size is required"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let display_handle = create_wayland_display_handle(display_id);
 | 
				
			||||||
 | 
					        let glutin_display = unsafe { Display::new(display_handle) }?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let config_template = self.config_template.unwrap_or_default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let config = select_config(&glutin_display, config_template)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let context_attributes = self.context_attributes.unwrap_or_default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let context = create_context(&glutin_display, &config, context_attributes)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let surface_handle = create_surface_handle(surface_id);
 | 
				
			||||||
 | 
					        let surface = create_surface(&glutin_display, &config, surface_handle, size)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let context = context
 | 
				
			||||||
 | 
					            .make_current(&surface)
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Unable to activate EGL context: {}. This may indicate a problem with the graphics drivers.", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(EGLContext { context, surface })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl EGLContext {
 | 
				
			||||||
 | 
					    pub fn builder() -> EGLContextBuilder {
 | 
				
			||||||
 | 
					        EGLContextBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn ensure_current(&self) -> Result<()> {
 | 
				
			||||||
 | 
					        if !self.context.is_current() {
 | 
				
			||||||
 | 
					            self.context.make_current(&self.surface)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn create_wayland_display_handle(display_id: ObjectId) -> RawDisplayHandle {
 | 
				
			||||||
 | 
					    let display =
 | 
				
			||||||
 | 
					        NonNull::new(display_id.as_ptr() as *mut c_void).expect("NonNull pointer creation failed");
 | 
				
			||||||
 | 
					    let handle = WaylandDisplayHandle::new(display);
 | 
				
			||||||
 | 
					    RawDisplayHandle::Wayland(handle)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn select_config(
 | 
				
			||||||
 | 
					    glutin_display: &Display,
 | 
				
			||||||
 | 
					    config_template: ConfigTemplateBuilder,
 | 
				
			||||||
 | 
					) -> Result<glutin::api::egl::config::Config> {
 | 
				
			||||||
 | 
					    let mut configs = unsafe { glutin_display.find_configs(config_template.build()) }?;
 | 
				
			||||||
 | 
					    configs
 | 
				
			||||||
 | 
					        .next()
 | 
				
			||||||
 | 
					        .ok_or_else(|| anyhow!("No compatible EGL configurations found."))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					fn create_context(
 | 
				
			||||||
 | 
					    glutin_display: &Display,
 | 
				
			||||||
 | 
					    config: &glutin::api::egl::config::Config,
 | 
				
			||||||
 | 
					    context_attributes: ContextAttributesBuilder,
 | 
				
			||||||
 | 
					) -> Result<glutin::api::egl::context::NotCurrentContext> {
 | 
				
			||||||
 | 
					    unsafe { glutin_display.create_context(config, &context_attributes.build(None)) }
 | 
				
			||||||
 | 
					        .map_err(|e| anyhow!("Failed to create context: {}", e))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn create_surface_handle(surface_id: ObjectId) -> RawWindowHandle {
 | 
				
			||||||
 | 
					    let surface =
 | 
				
			||||||
 | 
					        NonNull::new(surface_id.as_ptr() as *mut c_void).expect("NonNull pointer creation failed");
 | 
				
			||||||
 | 
					    let handle = WaylandWindowHandle::new(surface);
 | 
				
			||||||
 | 
					    RawWindowHandle::Wayland(handle)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn create_surface(
 | 
				
			||||||
 | 
					    glutin_display: &Display,
 | 
				
			||||||
 | 
					    config: &glutin::api::egl::config::Config,
 | 
				
			||||||
 | 
					    surface_handle: RawWindowHandle,
 | 
				
			||||||
 | 
					    size: LayerSize,
 | 
				
			||||||
 | 
					) -> Result<Surface<WindowSurface>> {
 | 
				
			||||||
 | 
					    let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
 | 
				
			||||||
 | 
					        surface_handle,
 | 
				
			||||||
 | 
					        NonZeroU32::new(size.physical_size().width).unwrap(),
 | 
				
			||||||
 | 
					        NonZeroU32::new(size.physical_size().height).unwrap(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    unsafe { glutin_display.create_window_surface(config, &attrs) }
 | 
				
			||||||
 | 
					        .map_err(|e| anyhow!("Failed to create window surface: {}", e))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsafe impl OpenGLInterface for EGLContext {
 | 
				
			||||||
 | 
					    fn ensure_current(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
				
			||||||
 | 
					        Ok(self.ensure_current()?)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn swap_buffers(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
				
			||||||
 | 
					        self.surface
 | 
				
			||||||
 | 
					            .swap_buffers(&self.context)
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Failed to swap buffers: {}", e))
 | 
				
			||||||
 | 
					            .map_err(Into::into)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn resize(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        width: NonZeroU32,
 | 
				
			||||||
 | 
					        height: NonZeroU32,
 | 
				
			||||||
 | 
					    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
				
			||||||
 | 
					        self.ensure_current()?;
 | 
				
			||||||
 | 
					        self.surface.resize(&self.context, width, height);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_proc_address(&self, name: &CStr) -> *const ffi::c_void {
 | 
				
			||||||
 | 
					        self.context.display().get_proc_address(name)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								src/rendering/femtovg_window.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/rendering/femtovg_window.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					use log::info;
 | 
				
			||||||
 | 
					use slint::{
 | 
				
			||||||
 | 
					    platform::{femtovg_renderer::FemtoVGRenderer, Renderer, WindowAdapter, WindowEvent},
 | 
				
			||||||
 | 
					    PhysicalSize, Window, WindowSize,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use std::cell::{Cell, RefCell};
 | 
				
			||||||
 | 
					use std::rc::{Rc, Weak};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct FemtoVGWindow {
 | 
				
			||||||
 | 
					    window: RefCell<Window>,
 | 
				
			||||||
 | 
					    renderer: RefCell<FemtoVGRenderer>,
 | 
				
			||||||
 | 
					    is_dirty: Cell<bool>,
 | 
				
			||||||
 | 
					    size: Cell<PhysicalSize>,
 | 
				
			||||||
 | 
					    scale_factor: Cell<f32>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FemtoVGWindow {
 | 
				
			||||||
 | 
					    pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> {
 | 
				
			||||||
 | 
					        Rc::new_cyclic(|weak_self| {
 | 
				
			||||||
 | 
					            let window = Window::new(weak_self.clone() as Weak<dyn WindowAdapter>);
 | 
				
			||||||
 | 
					            Self {
 | 
				
			||||||
 | 
					                window: RefCell::new(window),
 | 
				
			||||||
 | 
					                renderer: RefCell::new(renderer),
 | 
				
			||||||
 | 
					                is_dirty: Default::default(),
 | 
				
			||||||
 | 
					                size: Cell::new(PhysicalSize::default()),
 | 
				
			||||||
 | 
					                scale_factor: Cell::new(1.),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn render_frame_if_dirty(&self) {
 | 
				
			||||||
 | 
					        if self.is_dirty.get() {
 | 
				
			||||||
 | 
					            match self.renderer.borrow_mut().render() {
 | 
				
			||||||
 | 
					                Ok(_) => {} //log::debug!("Frame rendered successfully"),
 | 
				
			||||||
 | 
					                Err(e) => log::error!("Error rendering frame: {}", e),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.is_dirty.set(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_scale_factor(&self, scale_factor: f32) {
 | 
				
			||||||
 | 
					        info!("Setting scale factor to {}", scale_factor);
 | 
				
			||||||
 | 
					        self.scale_factor.set(scale_factor);
 | 
				
			||||||
 | 
					        self.window()
 | 
				
			||||||
 | 
					            .dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn scale_factor(&self) -> f32 {
 | 
				
			||||||
 | 
					        self.scale_factor.get()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowAdapter for FemtoVGWindow {
 | 
				
			||||||
 | 
					    fn window(&self) -> &Window {
 | 
				
			||||||
 | 
					        unsafe { self.window.as_ptr().as_ref().unwrap() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn renderer(&self) -> &dyn Renderer {
 | 
				
			||||||
 | 
					        unsafe { &*self.renderer.as_ptr() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size(&self) -> PhysicalSize {
 | 
				
			||||||
 | 
					        self.size.get()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_size(&self, size: WindowSize) {
 | 
				
			||||||
 | 
					        self.size.set(size.to_physical(self.scale_factor()));
 | 
				
			||||||
 | 
					        self.window.borrow().dispatch_event(WindowEvent::Resized {
 | 
				
			||||||
 | 
					            size: size.to_logical(self.scale_factor()),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn request_redraw(&self) {
 | 
				
			||||||
 | 
					        self.is_dirty.set(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::ops::Deref for FemtoVGWindow {
 | 
				
			||||||
 | 
					    type Target = Window;
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        unsafe { self.window.as_ptr().as_ref().unwrap() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/rendering/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/rendering/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					pub mod egl_context;
 | 
				
			||||||
 | 
					pub mod femtovg_window;
 | 
				
			||||||
 | 
					pub mod slint_platform;
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/rendering/slint_platform.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/rendering/slint_platform.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					use slint::{
 | 
				
			||||||
 | 
					    platform::{Platform, WindowAdapter},
 | 
				
			||||||
 | 
					    PlatformError,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use std::rc::{Rc, Weak};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::femtovg_window::FemtoVGWindow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct CustomSlintPlatform {
 | 
				
			||||||
 | 
					    window: Weak<FemtoVGWindow>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl CustomSlintPlatform {
 | 
				
			||||||
 | 
					    pub fn new(window: &Rc<FemtoVGWindow>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            window: Rc::downgrade(window),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Platform for CustomSlintPlatform {
 | 
				
			||||||
 | 
					    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
 | 
				
			||||||
 | 
					        self.window
 | 
				
			||||||
 | 
					            .upgrade()
 | 
				
			||||||
 | 
					            .map(|window| -> Rc<dyn WindowAdapter> { window })
 | 
				
			||||||
 | 
					            .ok_or_else(|| {
 | 
				
			||||||
 | 
					                PlatformError::Other(
 | 
				
			||||||
 | 
					                    "Failed to create window adapter: window no longer exists".into(),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										229
									
								
								src/windowing/event_handler.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/windowing/event_handler.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,229 @@
 | 
				
			||||||
 | 
					use crate::impl_empty_dispatch;
 | 
				
			||||||
 | 
					use log::info;
 | 
				
			||||||
 | 
					use slint::platform::{PointerEventButton, WindowEvent};
 | 
				
			||||||
 | 
					use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
 | 
				
			||||||
 | 
					    zwlr_layer_shell_v1::ZwlrLayerShellV1,
 | 
				
			||||||
 | 
					    zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use std::rc::Rc;
 | 
				
			||||||
 | 
					use std::{cell::RefCell, rc::Weak};
 | 
				
			||||||
 | 
					use wayland_client::{
 | 
				
			||||||
 | 
					    globals::GlobalListContents,
 | 
				
			||||||
 | 
					    protocol::{
 | 
				
			||||||
 | 
					        wl_compositor::WlCompositor,
 | 
				
			||||||
 | 
					        wl_output::{self, WlOutput},
 | 
				
			||||||
 | 
					        wl_pointer::{self, WlPointer},
 | 
				
			||||||
 | 
					        wl_registry::WlRegistry,
 | 
				
			||||||
 | 
					        wl_seat::WlSeat,
 | 
				
			||||||
 | 
					        wl_surface::WlSurface,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Connection, Dispatch, QueueHandle,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::state::WindowState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					pub struct WindowEventHandler {
 | 
				
			||||||
 | 
					    state: Weak<RefCell<WindowState>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowEventHandler {
 | 
				
			||||||
 | 
					    pub fn new(state: Weak<RefCell<WindowState>>) -> Self {
 | 
				
			||||||
 | 
					        Self { state }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn state(&self) -> Rc<RefCell<WindowState>> {
 | 
				
			||||||
 | 
					        self.state.upgrade().unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_pointer_enter(&mut self, surface_x: f64, surface_y: f64) {
 | 
				
			||||||
 | 
					        if let Some(state) = self.state.upgrade() {
 | 
				
			||||||
 | 
					            state
 | 
				
			||||||
 | 
					                .borrow()
 | 
				
			||||||
 | 
					                .set_current_pointer_position(surface_x, surface_y);
 | 
				
			||||||
 | 
					            if let Some(window) = state.borrow().window() {
 | 
				
			||||||
 | 
					                let logical_position = state.borrow().current_pointer_position();
 | 
				
			||||||
 | 
					                window.dispatch_event(WindowEvent::PointerMoved {
 | 
				
			||||||
 | 
					                    position: logical_position,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_pointer_leave(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(state) = self.state.upgrade() {
 | 
				
			||||||
 | 
					            if let Some(window) = state.borrow().window() {
 | 
				
			||||||
 | 
					                window.dispatch_event(WindowEvent::PointerExited);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_pointer_motion(&mut self, surface_x: f64, surface_y: f64) {
 | 
				
			||||||
 | 
					        if let Some(state) = self.state.upgrade() {
 | 
				
			||||||
 | 
					            state
 | 
				
			||||||
 | 
					                .borrow()
 | 
				
			||||||
 | 
					                .set_current_pointer_position(surface_x, surface_y);
 | 
				
			||||||
 | 
					            if let Some(window) = state.borrow().window() {
 | 
				
			||||||
 | 
					                let logical_position = state.borrow().current_pointer_position();
 | 
				
			||||||
 | 
					                window.dispatch_event(WindowEvent::PointerMoved {
 | 
				
			||||||
 | 
					                    position: logical_position,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_pointer_button(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        button_state: wayland_client::WEnum<wl_pointer::ButtonState>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        if let Some(state) = self.state.upgrade() {
 | 
				
			||||||
 | 
					            let is_press = matches!(
 | 
				
			||||||
 | 
					                button_state,
 | 
				
			||||||
 | 
					                wayland_client::WEnum::Value(wl_pointer::ButtonState::Pressed)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            let current_position = state.borrow().current_pointer_position();
 | 
				
			||||||
 | 
					            if let Some(window) = state.borrow().window() {
 | 
				
			||||||
 | 
					                let event = if is_press {
 | 
				
			||||||
 | 
					                    WindowEvent::PointerPressed {
 | 
				
			||||||
 | 
					                        button: PointerEventButton::Left,
 | 
				
			||||||
 | 
					                        position: current_position,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    WindowEvent::PointerReleased {
 | 
				
			||||||
 | 
					                        button: PointerEventButton::Left,
 | 
				
			||||||
 | 
					                        position: current_position,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                window.dispatch_event(event);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Dispatch<ZwlrLayerSurfaceV1, ()> for WindowEventHandler {
 | 
				
			||||||
 | 
					    fn event(
 | 
				
			||||||
 | 
					        state: &mut Self,
 | 
				
			||||||
 | 
					        layer_surface: &ZwlrLayerSurfaceV1,
 | 
				
			||||||
 | 
					        event: zwlr_layer_surface_v1::Event,
 | 
				
			||||||
 | 
					        _data: &(),
 | 
				
			||||||
 | 
					        _conn: &Connection,
 | 
				
			||||||
 | 
					        _queue_handle: &QueueHandle<Self>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        match event {
 | 
				
			||||||
 | 
					            zwlr_layer_surface_v1::Event::Configure {
 | 
				
			||||||
 | 
					                serial,
 | 
				
			||||||
 | 
					                width,
 | 
				
			||||||
 | 
					                height,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                info!("Layer surface configured with size: {}x{}", width, height);
 | 
				
			||||||
 | 
					                layer_surface.ack_configure(serial);
 | 
				
			||||||
 | 
					                if let Some(state) = state.state.upgrade() {
 | 
				
			||||||
 | 
					                    let state_borrow = state.borrow();
 | 
				
			||||||
 | 
					                    if width > 0 && height > 0 {
 | 
				
			||||||
 | 
					                        state_borrow
 | 
				
			||||||
 | 
					                            .update_size(state_borrow.output_size().width, state_borrow.height());
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        let current_size = state_borrow.output_size();
 | 
				
			||||||
 | 
					                        state_borrow.update_size(current_size.width, current_size.height);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            zwlr_layer_surface_v1::Event::Closed => {
 | 
				
			||||||
 | 
					                info!("Layer surface closed");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Dispatch<WlOutput, ()> for WindowEventHandler {
 | 
				
			||||||
 | 
					    fn event(
 | 
				
			||||||
 | 
					        state: &mut Self,
 | 
				
			||||||
 | 
					        _proxy: &WlOutput,
 | 
				
			||||||
 | 
					        event: <WlOutput as wayland_client::Proxy>::Event,
 | 
				
			||||||
 | 
					        _data: &(),
 | 
				
			||||||
 | 
					        _conn: &Connection,
 | 
				
			||||||
 | 
					        _qhandle: &QueueHandle<Self>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        match event {
 | 
				
			||||||
 | 
					            wl_output::Event::Mode { width, height, .. } => {
 | 
				
			||||||
 | 
					                info!("WlOutput size changed to {}x{}", width, height);
 | 
				
			||||||
 | 
					                if let Some(state) = state.state.upgrade() {
 | 
				
			||||||
 | 
					                    let state_borrow = state.borrow();
 | 
				
			||||||
 | 
					                    state_borrow.set_output_size(width as u32, height as u32);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_output::Event::Description { ref description } => {
 | 
				
			||||||
 | 
					                info!("WlOutput description: {:?}", description);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_output::Event::Scale { ref factor } => {
 | 
				
			||||||
 | 
					                info!("WlOutput factor scale: {:?}", factor);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_output::Event::Name { ref name } => {
 | 
				
			||||||
 | 
					                info!("WlOutput name: {:?}", name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_output::Event::Geometry {
 | 
				
			||||||
 | 
					                x,
 | 
				
			||||||
 | 
					                y,
 | 
				
			||||||
 | 
					                physical_width,
 | 
				
			||||||
 | 
					                physical_height,
 | 
				
			||||||
 | 
					                subpixel,
 | 
				
			||||||
 | 
					                make,
 | 
				
			||||||
 | 
					                model,
 | 
				
			||||||
 | 
					                transform,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                info!("WlOutput geometry: x={}, y={}, physical_width={}, physical_height={}, subpixel={:?}, make={:?}, model={:?}, transform={:?}", x, y, physical_width, physical_height, subpixel, make, model, transform);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_output::Event::Done => {
 | 
				
			||||||
 | 
					                info!("WlOutput done");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Dispatch<WlPointer, ()> for WindowEventHandler {
 | 
				
			||||||
 | 
					    fn event(
 | 
				
			||||||
 | 
					        state: &mut Self,
 | 
				
			||||||
 | 
					        _proxy: &WlPointer,
 | 
				
			||||||
 | 
					        event: <WlPointer as wayland_client::Proxy>::Event,
 | 
				
			||||||
 | 
					        _data: &(),
 | 
				
			||||||
 | 
					        _conn: &Connection,
 | 
				
			||||||
 | 
					        _qhandle: &QueueHandle<Self>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        match event {
 | 
				
			||||||
 | 
					            wl_pointer::Event::Enter {
 | 
				
			||||||
 | 
					                surface_x,
 | 
				
			||||||
 | 
					                surface_y,
 | 
				
			||||||
 | 
					                ..
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                state.handle_pointer_enter(surface_x, surface_y);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_pointer::Event::Leave { .. } => {
 | 
				
			||||||
 | 
					                state.handle_pointer_leave();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_pointer::Event::Motion {
 | 
				
			||||||
 | 
					                surface_x,
 | 
				
			||||||
 | 
					                surface_y,
 | 
				
			||||||
 | 
					                ..
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                state.handle_pointer_motion(surface_x, surface_y);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            wl_pointer::Event::Button {
 | 
				
			||||||
 | 
					                state: button_state,
 | 
				
			||||||
 | 
					                ..
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                state.handle_pointer_button(button_state);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl_empty_dispatch!(
 | 
				
			||||||
 | 
					    (WlRegistry, GlobalListContents),
 | 
				
			||||||
 | 
					    (WlCompositor, ()),
 | 
				
			||||||
 | 
					    (WlSurface, ()),
 | 
				
			||||||
 | 
					    (ZwlrLayerShellV1, ()),
 | 
				
			||||||
 | 
					    (WlSeat, ())
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
							
								
								
									
										110
									
								
								src/windowing/event_loop.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/windowing/event_loop.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,110 @@
 | 
				
			||||||
 | 
					use anyhow::{anyhow, Result};
 | 
				
			||||||
 | 
					use log::{debug, error, info};
 | 
				
			||||||
 | 
					use smithay_client_toolkit::reexports::calloop::{self, EventLoop, Interest, Mode, PostAction};
 | 
				
			||||||
 | 
					use std::cell::RefCell;
 | 
				
			||||||
 | 
					use std::rc::{Rc, Weak};
 | 
				
			||||||
 | 
					use wayland_client::{Connection, EventQueue};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::rendering::femtovg_window::FemtoVGWindow;
 | 
				
			||||||
 | 
					use crate::windowing::event_handler::WindowEventHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct EventLoopHandler {
 | 
				
			||||||
 | 
					    window: Weak<FemtoVGWindow>,
 | 
				
			||||||
 | 
					    wayland_queue: Weak<RefCell<EventQueue<WindowEventHandler>>>,
 | 
				
			||||||
 | 
					    connection: Weak<Connection>,
 | 
				
			||||||
 | 
					    event_handler: Weak<RefCell<WindowEventHandler>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl EventLoopHandler {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        window: Weak<FemtoVGWindow>,
 | 
				
			||||||
 | 
					        wayland_queue: Weak<RefCell<EventQueue<WindowEventHandler>>>,
 | 
				
			||||||
 | 
					        connection: Weak<Connection>,
 | 
				
			||||||
 | 
					        event_handler: Weak<RefCell<WindowEventHandler>>,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        debug!("Creating EventLoopHandler");
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            window,
 | 
				
			||||||
 | 
					            wayland_queue,
 | 
				
			||||||
 | 
					            connection,
 | 
				
			||||||
 | 
					            event_handler,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn setup_wayland_event_source(&self, loop_handle: &calloop::LoopHandle<()>) -> Result<()> {
 | 
				
			||||||
 | 
					        debug!("Setting up Wayland event source");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let wayland_queue = self.wayland_queue.clone();
 | 
				
			||||||
 | 
					        let event_handler = self.event_handler.clone();
 | 
				
			||||||
 | 
					        let connection = self.connection.upgrade().ok_or_else(|| {
 | 
				
			||||||
 | 
					            anyhow!("Failed to get Wayland connection reference in Wayland event source")
 | 
				
			||||||
 | 
					        })?;
 | 
				
			||||||
 | 
					        let window = self.window.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loop_handle
 | 
				
			||||||
 | 
					            .insert_source(
 | 
				
			||||||
 | 
					                calloop::generic::Generic::new(connection, Interest::READ, Mode::Level),
 | 
				
			||||||
 | 
					                move |_, connection, _| {
 | 
				
			||||||
 | 
					                    let result: Result<PostAction, anyhow::Error> = (|| {
 | 
				
			||||||
 | 
					                        let wayland_queue = wayland_queue
 | 
				
			||||||
 | 
					                            .upgrade()
 | 
				
			||||||
 | 
					                            .ok_or_else(|| anyhow!("Failed to get Wayland queue reference"))?;
 | 
				
			||||||
 | 
					                        let event_handler = event_handler
 | 
				
			||||||
 | 
					                            .upgrade()
 | 
				
			||||||
 | 
					                            .ok_or_else(|| anyhow!("Failed to get event handler reference"))?;
 | 
				
			||||||
 | 
					                        let window = window
 | 
				
			||||||
 | 
					                            .upgrade()
 | 
				
			||||||
 | 
					                            .ok_or_else(|| anyhow!("Failed to get window reference"))?;
 | 
				
			||||||
 | 
					                        Self::handle_wayland_events(
 | 
				
			||||||
 | 
					                            connection,
 | 
				
			||||||
 | 
					                            &wayland_queue,
 | 
				
			||||||
 | 
					                            &event_handler,
 | 
				
			||||||
 | 
					                            &window,
 | 
				
			||||||
 | 
					                        )?;
 | 
				
			||||||
 | 
					                        Ok(PostAction::Continue)
 | 
				
			||||||
 | 
					                    })();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    result.map_err(|e| {
 | 
				
			||||||
 | 
					                        error!("Error handling Wayland events: {}", e);
 | 
				
			||||||
 | 
					                        std::io::Error::new(std::io::ErrorKind::Other, e)
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Failed to insert Wayland event source: {}", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn run(&self, event_loop: &mut EventLoop<()>) -> Result<()> {
 | 
				
			||||||
 | 
					        info!("Starting event loop");
 | 
				
			||||||
 | 
					        event_loop
 | 
				
			||||||
 | 
					            .run(None, &mut (), |_| {})
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Failed to run event loop: {}", e))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_wayland_events(
 | 
				
			||||||
 | 
					        connection: &Connection,
 | 
				
			||||||
 | 
					        wayland_queue: &Rc<RefCell<EventQueue<WindowEventHandler>>>,
 | 
				
			||||||
 | 
					        event_handler: &Rc<RefCell<WindowEventHandler>>,
 | 
				
			||||||
 | 
					        window: &Rc<FemtoVGWindow>,
 | 
				
			||||||
 | 
					    ) -> Result<()> {
 | 
				
			||||||
 | 
					        connection
 | 
				
			||||||
 | 
					            .flush()
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Failed to flush connection: {}", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut event_queue = wayland_queue.borrow_mut();
 | 
				
			||||||
 | 
					        if let Some(guard) = event_queue.prepare_read() {
 | 
				
			||||||
 | 
					            guard
 | 
				
			||||||
 | 
					                .read()
 | 
				
			||||||
 | 
					                .map_err(|e| anyhow!("Failed to read Wayland events: {}", e))?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        event_queue
 | 
				
			||||||
 | 
					            .dispatch_pending(&mut *event_handler.borrow_mut())
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow!("Failed to dispatch Wayland events: {}", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        slint::platform::update_timers_and_animations();
 | 
				
			||||||
 | 
					        window.render_frame_if_dirty();
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/windowing/macros.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/windowing/macros.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! impl_empty_dispatch {
 | 
				
			||||||
 | 
					    ($(($t:ty, $u:ty)),+) => {
 | 
				
			||||||
 | 
					        $(
 | 
				
			||||||
 | 
					            impl Dispatch<$t, $u> for WindowEventHandler {
 | 
				
			||||||
 | 
					                fn event(
 | 
				
			||||||
 | 
					                    _state: &mut Self,
 | 
				
			||||||
 | 
					                    _proxy: &$t,
 | 
				
			||||||
 | 
					                    _event: <$t as wayland_client::Proxy>::Event,
 | 
				
			||||||
 | 
					                    _data: &$u,
 | 
				
			||||||
 | 
					                    _conn: &Connection,
 | 
				
			||||||
 | 
					                    _qhandle: &QueueHandle<Self>,
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                  info!("Implement empty dispatch event for {:?}", stringify!($t));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )+
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					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, ())
 | 
				
			||||||
 | 
					                    .with_context(|| format!("Failed to bind {}", stringify!($name)))?;
 | 
				
			||||||
 | 
					            )+
 | 
				
			||||||
 | 
					            Ok(($($name),+))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										403
									
								
								src/windowing/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								src/windowing/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,403 @@
 | 
				
			||||||
 | 
					use anyhow::{Context, Result};
 | 
				
			||||||
 | 
					use log::{debug, info};
 | 
				
			||||||
 | 
					use slint::{platform::femtovg_renderer::FemtoVGRenderer, ComponentHandle, LogicalPosition};
 | 
				
			||||||
 | 
					use slint_interpreter::{ComponentDefinition, ComponentInstance};
 | 
				
			||||||
 | 
					use smithay_client_toolkit::reexports::{
 | 
				
			||||||
 | 
					    calloop::{self, EventLoop},
 | 
				
			||||||
 | 
					    protocols_wlr::layer_shell::v1::client::{
 | 
				
			||||||
 | 
					        zwlr_layer_shell_v1::{self, ZwlrLayerShellV1},
 | 
				
			||||||
 | 
					        zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity, ZwlrLayerSurfaceV1},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use std::{cell::RefCell, rc::Rc};
 | 
				
			||||||
 | 
					use wayland_client::{
 | 
				
			||||||
 | 
					    globals::{registry_queue_init, GlobalList},
 | 
				
			||||||
 | 
					    protocol::{
 | 
				
			||||||
 | 
					        wl_compositor::WlCompositor, wl_display::WlDisplay, wl_output::WlOutput, wl_seat::WlSeat,
 | 
				
			||||||
 | 
					        wl_surface::WlSurface,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Connection, EventQueue, Proxy, QueueHandle,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    bind_globals,
 | 
				
			||||||
 | 
					    common::LayerSize,
 | 
				
			||||||
 | 
					    rendering::{
 | 
				
			||||||
 | 
					        egl_context::EGLContext, femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod event_handler;
 | 
				
			||||||
 | 
					mod event_loop;
 | 
				
			||||||
 | 
					mod macros;
 | 
				
			||||||
 | 
					mod state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use self::{event_handler::WindowEventHandler, event_loop::EventLoopHandler, state::WindowState};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct WindowConfig {
 | 
				
			||||||
 | 
					    height: u32,
 | 
				
			||||||
 | 
					    layer: zwlr_layer_shell_v1::Layer,
 | 
				
			||||||
 | 
					    margin: (i32, i32, i32, i32),
 | 
				
			||||||
 | 
					    anchor: Anchor,
 | 
				
			||||||
 | 
					    keyboard_interactivity: KeyboardInteractivity,
 | 
				
			||||||
 | 
					    exclusive_zone: i32,
 | 
				
			||||||
 | 
					    scale_factor: f32,
 | 
				
			||||||
 | 
					    namespace: String,
 | 
				
			||||||
 | 
					    component_definition: Option<Rc<ComponentDefinition>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for WindowConfig {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            height: 30,
 | 
				
			||||||
 | 
					            layer: zwlr_layer_shell_v1::Layer::Top,
 | 
				
			||||||
 | 
					            margin: (0, 0, 0, 0),
 | 
				
			||||||
 | 
					            anchor: Anchor::Top | Anchor::Left | Anchor::Right,
 | 
				
			||||||
 | 
					            keyboard_interactivity: KeyboardInteractivity::OnDemand,
 | 
				
			||||||
 | 
					            exclusive_zone: -1,
 | 
				
			||||||
 | 
					            namespace: "layer-sheeka".to_string(),
 | 
				
			||||||
 | 
					            scale_factor: 1.0,
 | 
				
			||||||
 | 
					            component_definition: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct WindowingSystemBuilder {
 | 
				
			||||||
 | 
					    config: WindowConfig,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for WindowingSystemBuilder {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowingSystemBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            config: WindowConfig::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_height(mut self, height: u32) -> Self {
 | 
				
			||||||
 | 
					        self.config.height = height;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_layer(mut self, layer: zwlr_layer_shell_v1::Layer) -> Self {
 | 
				
			||||||
 | 
					        self.config.layer = layer;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_margin(mut self, top: i32, right: i32, bottom: i32, left: i32) -> Self {
 | 
				
			||||||
 | 
					        self.config.margin = (top, right, bottom, left);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_anchor(mut self, anchor: Anchor) -> Self {
 | 
				
			||||||
 | 
					        self.config.anchor = anchor;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_keyboard_interactivity(mut self, interactivity: KeyboardInteractivity) -> Self {
 | 
				
			||||||
 | 
					        self.config.keyboard_interactivity = interactivity;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_exclusive_zone(mut self, zone: i32) -> Self {
 | 
				
			||||||
 | 
					        self.config.exclusive_zone = zone;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_namespace(mut self, namespace: String) -> Self {
 | 
				
			||||||
 | 
					        self.config.namespace = namespace;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_scale_factor(mut self, scale_factor: f32) -> Self {
 | 
				
			||||||
 | 
					        self.config.scale_factor = scale_factor;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_component_definition(mut self, component: Rc<ComponentDefinition>) -> Self {
 | 
				
			||||||
 | 
					        self.config.component_definition = Some(component);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<WindowingSystem<'static>> {
 | 
				
			||||||
 | 
					        if self.config.component_definition.is_none() {
 | 
				
			||||||
 | 
					            return Err(anyhow::anyhow!("Slint component not set"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WindowingSystem::new(self.config)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct WindowingSystem<'a> {
 | 
				
			||||||
 | 
					    state: Rc<RefCell<WindowState>>,
 | 
				
			||||||
 | 
					    connection: Rc<Connection>,
 | 
				
			||||||
 | 
					    event_handler: Rc<RefCell<WindowEventHandler>>,
 | 
				
			||||||
 | 
					    window: Option<Rc<FemtoVGWindow>>,
 | 
				
			||||||
 | 
					    event_queue: Rc<RefCell<EventQueue<WindowEventHandler>>>,
 | 
				
			||||||
 | 
					    component_instance: Option<Rc<ComponentInstance>>,
 | 
				
			||||||
 | 
					    display: WlDisplay,
 | 
				
			||||||
 | 
					    config: WindowConfig,
 | 
				
			||||||
 | 
					    event_loop: EventLoop<'a, ()>,
 | 
				
			||||||
 | 
					    event_loop_handler: Option<EventLoopHandler>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> WindowingSystem<'a> {
 | 
				
			||||||
 | 
					    fn new(config: WindowConfig) -> Result<Self> {
 | 
				
			||||||
 | 
					        info!("Initializing WindowingSystem");
 | 
				
			||||||
 | 
					        let connection = Rc::new(Connection::connect_to_env()?);
 | 
				
			||||||
 | 
					        let state = Rc::new(RefCell::new(WindowState::new(&config)));
 | 
				
			||||||
 | 
					        let event_handler = Rc::new(RefCell::new(WindowEventHandler::new(Rc::downgrade(&state))));
 | 
				
			||||||
 | 
					        let display = connection.display();
 | 
				
			||||||
 | 
					        let event_queue = Rc::new(RefCell::new(connection.new_event_queue()));
 | 
				
			||||||
 | 
					        let global_list = Self::initialize_registry(&connection)?;
 | 
				
			||||||
 | 
					        let (compositor, output, layer_shell, seat) =
 | 
				
			||||||
 | 
					            Self::bind_globals(&global_list, &event_queue.borrow().handle())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self::setup_surface(
 | 
				
			||||||
 | 
					            &compositor,
 | 
				
			||||||
 | 
					            &output,
 | 
				
			||||||
 | 
					            &layer_shell,
 | 
				
			||||||
 | 
					            &seat,
 | 
				
			||||||
 | 
					            &event_queue.borrow().handle(),
 | 
				
			||||||
 | 
					            &event_handler,
 | 
				
			||||||
 | 
					            &config,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let event_loop = EventLoop::try_new().context("Failed to create event loop")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut system = Self {
 | 
				
			||||||
 | 
					            state,
 | 
				
			||||||
 | 
					            connection,
 | 
				
			||||||
 | 
					            event_handler,
 | 
				
			||||||
 | 
					            window: None,
 | 
				
			||||||
 | 
					            event_queue,
 | 
				
			||||||
 | 
					            component_instance: None,
 | 
				
			||||||
 | 
					            display,
 | 
				
			||||||
 | 
					            config,
 | 
				
			||||||
 | 
					            event_loop,
 | 
				
			||||||
 | 
					            event_loop_handler: None,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        system.wait_for_configure()?;
 | 
				
			||||||
 | 
					        system.initialize_renderer_and_ui()?;
 | 
				
			||||||
 | 
					        system.initialize_event_loop_handler()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(system)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initialize_registry(connection: &Connection) -> Result<GlobalList> {
 | 
				
			||||||
 | 
					        registry_queue_init::<WindowEventHandler>(connection)
 | 
				
			||||||
 | 
					            .map(|(global_list, _)| global_list)
 | 
				
			||||||
 | 
					            .context("Failed to initialize registry")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn bind_globals(
 | 
				
			||||||
 | 
					        global_list: &GlobalList,
 | 
				
			||||||
 | 
					        queue_handle: &QueueHandle<WindowEventHandler>,
 | 
				
			||||||
 | 
					    ) -> Result<(WlCompositor, WlOutput, ZwlrLayerShellV1, WlSeat)> {
 | 
				
			||||||
 | 
					        bind_globals!(
 | 
				
			||||||
 | 
					            global_list,
 | 
				
			||||||
 | 
					            queue_handle,
 | 
				
			||||||
 | 
					            (WlCompositor, compositor, 1..=1),
 | 
				
			||||||
 | 
					            (WlOutput, output, 1..=1),
 | 
				
			||||||
 | 
					            (ZwlrLayerShellV1, layer_shell, 1..=1),
 | 
				
			||||||
 | 
					            (WlSeat, seat, 1..=1)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn setup_surface(
 | 
				
			||||||
 | 
					        compositor: &WlCompositor,
 | 
				
			||||||
 | 
					        output: &WlOutput,
 | 
				
			||||||
 | 
					        layer_shell: &ZwlrLayerShellV1,
 | 
				
			||||||
 | 
					        seat: &WlSeat,
 | 
				
			||||||
 | 
					        queue_handle: &QueueHandle<WindowEventHandler>,
 | 
				
			||||||
 | 
					        event_handler: &Rc<RefCell<WindowEventHandler>>,
 | 
				
			||||||
 | 
					        config: &WindowConfig,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let surface = compositor.create_surface(queue_handle, ());
 | 
				
			||||||
 | 
					        let layer_surface = Rc::new(layer_shell.get_layer_surface(
 | 
				
			||||||
 | 
					            &surface,
 | 
				
			||||||
 | 
					            Some(output),
 | 
				
			||||||
 | 
					            config.layer,
 | 
				
			||||||
 | 
					            config.namespace.clone(),
 | 
				
			||||||
 | 
					            queue_handle,
 | 
				
			||||||
 | 
					            (),
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pointer = seat.get_pointer(queue_handle, ());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let binding = event_handler.borrow_mut();
 | 
				
			||||||
 | 
					        let binding = binding.state();
 | 
				
			||||||
 | 
					        let mut state = binding.borrow_mut();
 | 
				
			||||||
 | 
					        state.set_surface(surface.clone());
 | 
				
			||||||
 | 
					        state.set_layer_surface(layer_surface.clone());
 | 
				
			||||||
 | 
					        state.set_pointer(pointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self::configure_layer_surface(&layer_surface, &surface, config);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn configure_layer_surface(
 | 
				
			||||||
 | 
					        layer_surface: &Rc<ZwlrLayerSurfaceV1>,
 | 
				
			||||||
 | 
					        surface: &WlSurface,
 | 
				
			||||||
 | 
					        config: &WindowConfig,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        layer_surface.set_anchor(config.anchor);
 | 
				
			||||||
 | 
					        layer_surface.set_margin(
 | 
				
			||||||
 | 
					            config.margin.0,
 | 
				
			||||||
 | 
					            config.margin.1,
 | 
				
			||||||
 | 
					            config.margin.2,
 | 
				
			||||||
 | 
					            config.margin.3,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        println!("Setting exclusive zone: {}", config.exclusive_zone);
 | 
				
			||||||
 | 
					        layer_surface.set_exclusive_zone(config.exclusive_zone);
 | 
				
			||||||
 | 
					        layer_surface.set_keyboard_interactivity(config.keyboard_interactivity);
 | 
				
			||||||
 | 
					        layer_surface.set_size(1, config.height);
 | 
				
			||||||
 | 
					        surface.commit();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn wait_for_configure(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        info!("Waiting for surface to be configured...");
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            self.connection.flush()?;
 | 
				
			||||||
 | 
					            self.event_queue
 | 
				
			||||||
 | 
					                .borrow_mut()
 | 
				
			||||||
 | 
					                .blocking_dispatch(&mut self.event_handler.borrow_mut())
 | 
				
			||||||
 | 
					                .context("Failed to dispatch events")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let state = self.state.borrow();
 | 
				
			||||||
 | 
					            let size = state.output_size();
 | 
				
			||||||
 | 
					            if size.width > 1 && size.height > 1 {
 | 
				
			||||||
 | 
					                info!("Configured output size: {:?}", size);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        debug!("Surface configuration complete");
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initialize_renderer_and_ui(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        let renderer = self.create_renderer()?;
 | 
				
			||||||
 | 
					        let component_definition = self
 | 
				
			||||||
 | 
					            .config
 | 
				
			||||||
 | 
					            .component_definition
 | 
				
			||||||
 | 
					            .clone()
 | 
				
			||||||
 | 
					            .ok_or_else(|| anyhow::anyhow!("Component definition not set"))?;
 | 
				
			||||||
 | 
					        let (window, component_instance) =
 | 
				
			||||||
 | 
					            self.initialize_slint_ui(renderer, component_definition)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.window = Some(window.clone());
 | 
				
			||||||
 | 
					        self.state.borrow_mut().set_window(Rc::downgrade(&window));
 | 
				
			||||||
 | 
					        self.component_instance = Some(component_instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn create_renderer(&self) -> Result<FemtoVGRenderer> {
 | 
				
			||||||
 | 
					        let size = self.state.borrow().size();
 | 
				
			||||||
 | 
					        let binding = self.state.borrow();
 | 
				
			||||||
 | 
					        let surface = binding
 | 
				
			||||||
 | 
					            .surface()
 | 
				
			||||||
 | 
					            .ok_or_else(|| anyhow::anyhow!("Surface not initialized"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug!("Creating EGL context with size: {:?}", size);
 | 
				
			||||||
 | 
					        let context = EGLContext::builder()
 | 
				
			||||||
 | 
					            .with_display_id(self.display.id())
 | 
				
			||||||
 | 
					            .with_surface_id(surface.id())
 | 
				
			||||||
 | 
					            .with_size(LayerSize::new(size.width, size.height))
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					            .context("Failed to build EGL context")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug!("Creating FemtoVGRenderer");
 | 
				
			||||||
 | 
					        FemtoVGRenderer::new(context).context("Failed to create FemtoVGRenderer")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initialize_slint_ui(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        renderer: FemtoVGRenderer,
 | 
				
			||||||
 | 
					        component_definition: Rc<ComponentDefinition>,
 | 
				
			||||||
 | 
					    ) -> Result<(Rc<FemtoVGWindow>, Rc<ComponentInstance>)> {
 | 
				
			||||||
 | 
					        let femtovg_window = FemtoVGWindow::new(renderer);
 | 
				
			||||||
 | 
					        let size = self.state.borrow().size();
 | 
				
			||||||
 | 
					        info!("Initializing UI with size: {:?}", size);
 | 
				
			||||||
 | 
					        femtovg_window.set_size(slint::WindowSize::Physical(size));
 | 
				
			||||||
 | 
					        femtovg_window.set_scale_factor(self.config.scale_factor);
 | 
				
			||||||
 | 
					        femtovg_window.set_position(LogicalPosition::new(0., 0.));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug!("Setting up custom Slint platform");
 | 
				
			||||||
 | 
					        let platform = CustomSlintPlatform::new(&femtovg_window);
 | 
				
			||||||
 | 
					        slint::platform::set_platform(Box::new(platform))
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow::anyhow!("Failed to set platform: {:?}", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug!("Creating Slint component instance");
 | 
				
			||||||
 | 
					        let slint_component: Rc<ComponentInstance> = Rc::new(component_definition.create()?);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        slint_component
 | 
				
			||||||
 | 
					            .show()
 | 
				
			||||||
 | 
					            .map_err(|e| anyhow::anyhow!("Failed to show component: {:?}", e))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((femtovg_window, slint_component))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn initialize_event_loop_handler(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        let event_loop_handler = EventLoopHandler::new(
 | 
				
			||||||
 | 
					            Rc::downgrade(self.window.as_ref().unwrap()),
 | 
				
			||||||
 | 
					            Rc::downgrade(&self.event_queue),
 | 
				
			||||||
 | 
					            Rc::downgrade(&self.connection),
 | 
				
			||||||
 | 
					            Rc::downgrade(&self.event_handler),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.event_loop_handler = Some(event_loop_handler);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn setup_event_sources(&self) -> Result<()> {
 | 
				
			||||||
 | 
					        let loop_handle = self.event_loop.handle();
 | 
				
			||||||
 | 
					        let event_loop_handler = self
 | 
				
			||||||
 | 
					            .event_loop_handler
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .ok_or_else(|| anyhow::anyhow!("EventLoopHandler not initialized"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        event_loop_handler.setup_wayland_event_source(&loop_handle)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn event_loop_handle(&self) -> calloop::LoopHandle<'a, ()> {
 | 
				
			||||||
 | 
					        self.event_loop.handle()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn run(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        info!("Starting WindowingSystem main loop");
 | 
				
			||||||
 | 
					        if let Some(window) = &self.window {
 | 
				
			||||||
 | 
					            window.render_frame_if_dirty();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let event_loop_handler = self
 | 
				
			||||||
 | 
					            .event_loop_handler
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .ok_or_else(|| anyhow::anyhow!("EventLoopHandler not initialized"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        event_loop_handler.run(&mut self.event_loop)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn component_instance(&self) -> Option<Rc<ComponentInstance>> {
 | 
				
			||||||
 | 
					        self.component_instance.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn window(&self) -> Option<Rc<FemtoVGWindow>> {
 | 
				
			||||||
 | 
					        self.window.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn state(&self) -> Rc<RefCell<WindowState>> {
 | 
				
			||||||
 | 
					        self.state.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn display(&self) -> &WlDisplay {
 | 
				
			||||||
 | 
					        &self.display
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/windowing/state.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/windowing/state.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					use std::{cell::Cell, rc::Weak};
 | 
				
			||||||
 | 
					use std::rc::Rc;
 | 
				
			||||||
 | 
					use log::info;
 | 
				
			||||||
 | 
					use slint::{LogicalPosition, PhysicalSize};
 | 
				
			||||||
 | 
					use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
 | 
				
			||||||
 | 
					use wayland_client::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface};
 | 
				
			||||||
 | 
					use crate::rendering::femtovg_window::FemtoVGWindow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::WindowConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct WindowState {
 | 
				
			||||||
 | 
					    surface: Option<WlSurface>,
 | 
				
			||||||
 | 
					    layer_surface: Option<Rc<ZwlrLayerSurfaceV1>>,
 | 
				
			||||||
 | 
					    size: Cell<PhysicalSize>,
 | 
				
			||||||
 | 
					    output_size: Cell<PhysicalSize>,
 | 
				
			||||||
 | 
					    pointer: Option<WlPointer>,
 | 
				
			||||||
 | 
					    window: Option<Weak<FemtoVGWindow>>,
 | 
				
			||||||
 | 
					    current_pointer_position: Cell<LogicalPosition>,
 | 
				
			||||||
 | 
					    scale_factor: f32,
 | 
				
			||||||
 | 
					    height: u32,
 | 
				
			||||||
 | 
					    exclusive_zone: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowState {
 | 
				
			||||||
 | 
					    pub fn new(config: &WindowConfig) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            surface: None,
 | 
				
			||||||
 | 
					            layer_surface: None,
 | 
				
			||||||
 | 
					            size: Cell::new(PhysicalSize::default()),
 | 
				
			||||||
 | 
					            output_size: Cell::new(PhysicalSize::default()),
 | 
				
			||||||
 | 
					            pointer: None,
 | 
				
			||||||
 | 
					            window: None,
 | 
				
			||||||
 | 
					            current_pointer_position: Cell::new(LogicalPosition::default()),
 | 
				
			||||||
 | 
					            scale_factor: config.scale_factor,
 | 
				
			||||||
 | 
					            height: config.height,
 | 
				
			||||||
 | 
					            exclusive_zone: config.exclusive_zone,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update_size(&self, width: u32, height: u32) {
 | 
				
			||||||
 | 
					        let new_size = PhysicalSize::new(width, height);
 | 
				
			||||||
 | 
					        self.size.set(new_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(window) = &self.window() {
 | 
				
			||||||
 | 
					            info!("Updating window size to {}x{}", width, height);
 | 
				
			||||||
 | 
					            window.set_size(slint::WindowSize::Physical(new_size));
 | 
				
			||||||
 | 
					            window.set_scale_factor(self.scale_factor);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(layer_surface) = &self.layer_surface() {
 | 
				
			||||||
 | 
					            info!("Updating layer surface size to {}x{}", width, height);
 | 
				
			||||||
 | 
					            layer_surface.set_size(width, height);
 | 
				
			||||||
 | 
					            layer_surface.set_exclusive_zone(self.exclusive_zone);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(s) = self.surface.as_ref() {
 | 
				
			||||||
 | 
					            s.commit()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_current_pointer_position(&self, physical_x: f64, physical_y: f64) {
 | 
				
			||||||
 | 
					        let scale_factor = self.scale_factor;
 | 
				
			||||||
 | 
					        let logical_position = LogicalPosition::new(
 | 
				
			||||||
 | 
					            physical_x as f32 / scale_factor,
 | 
				
			||||||
 | 
					            physical_y as f32 / scale_factor,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        self.current_pointer_position.set(logical_position);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn size(&self) -> PhysicalSize {
 | 
				
			||||||
 | 
					        self.size.get()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn output_size(&self) -> PhysicalSize {
 | 
				
			||||||
 | 
					        self.output_size.get()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn current_pointer_position(&self) -> LogicalPosition {
 | 
				
			||||||
 | 
					        self.current_pointer_position.get()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn window(&self) -> Option<Rc<FemtoVGWindow>> {
 | 
				
			||||||
 | 
					        self.window.as_ref().and_then(|w| w.upgrade())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn layer_surface(&self) -> Option<Rc<ZwlrLayerSurfaceV1>> {
 | 
				
			||||||
 | 
					        self.layer_surface.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn surface(&self) -> Option<&WlSurface> {
 | 
				
			||||||
 | 
					        self.surface.as_ref()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn height(&self) -> u32 {
 | 
				
			||||||
 | 
					        self.height
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_output_size(&self, width: u32, height: u32) {
 | 
				
			||||||
 | 
					        self.output_size.set(PhysicalSize::new(width, height));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn set_window(&mut self, window: Weak<FemtoVGWindow>) {
 | 
				
			||||||
 | 
					        self.window = Some(window);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_layer_surface(&mut self, layer_surface: Rc<ZwlrLayerSurfaceV1>) {
 | 
				
			||||||
 | 
					        self.layer_surface = Some(layer_surface);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_surface(&mut self, surface: WlSurface) {
 | 
				
			||||||
 | 
					        self.surface = Some(surface);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn set_pointer(&mut self, pointer: WlPointer) {
 | 
				
			||||||
 | 
					        self.pointer = Some(pointer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue