feat: add init project draft
parent
4697a83f18
commit
155d4071a5
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
mod common;
|
||||
mod rendering;
|
||||
mod windowing;
|
||||
|
||||
pub use windowing::WindowingSystemBuilder;
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod egl_context;
|
||||
pub mod femtovg_window;
|
||||
pub mod slint_platform;
|
|
@ -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(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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, ())
|
||||
);
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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),+))
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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…
Reference in New Issue