mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-10-28 06:34:24 +00:00
336 lines
10 KiB
Rust
336 lines
10 KiB
Rust
use self::state::WindowState;
|
|
use crate::{
|
|
bind_globals,
|
|
errors::{LayerShikaError, Result},
|
|
rendering::{egl_context::EGLContext, femtovg_window::FemtoVGWindow},
|
|
};
|
|
use config::WindowConfig;
|
|
use log::{debug, error, info};
|
|
use slint::{
|
|
platform::{femtovg_renderer::FemtoVGRenderer, update_timers_and_animations},
|
|
LogicalPosition, PhysicalSize,
|
|
};
|
|
use slint_interpreter::ComponentInstance;
|
|
use smithay_client_toolkit::reexports::{
|
|
calloop::{generic::Generic, EventLoop, Interest, LoopHandle, Mode, PostAction},
|
|
protocols_wlr::layer_shell::v1::client::{
|
|
zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
|
},
|
|
};
|
|
use state::builder::WindowStateBuilder;
|
|
use std::rc::Rc;
|
|
use wayland_client::{
|
|
globals::registry_queue_init,
|
|
protocol::{
|
|
wl_compositor::WlCompositor, wl_display::WlDisplay, wl_output::WlOutput, wl_seat::WlSeat,
|
|
wl_surface::WlSurface,
|
|
},
|
|
Connection, EventQueue, Proxy, QueueHandle,
|
|
};
|
|
use wayland_protocols::wp::fractional_scale::v1::client::{
|
|
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
|
wp_fractional_scale_v1::WpFractionalScaleV1,
|
|
};
|
|
use wayland_protocols::wp::viewporter::client::{
|
|
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
|
};
|
|
|
|
pub mod builder;
|
|
mod config;
|
|
mod macros;
|
|
mod state;
|
|
|
|
type GlobalObjects = (
|
|
WlCompositor,
|
|
WlOutput,
|
|
ZwlrLayerShellV1,
|
|
WlSeat,
|
|
Option<WpFractionalScaleManagerV1>,
|
|
Option<WpViewporter>,
|
|
);
|
|
|
|
type SurfaceObjects = (
|
|
Rc<WlSurface>,
|
|
Rc<ZwlrLayerSurfaceV1>,
|
|
Option<Rc<WpFractionalScaleV1>>,
|
|
Option<Rc<WpViewport>>,
|
|
);
|
|
|
|
pub struct WindowingSystem {
|
|
state: WindowState,
|
|
connection: Rc<Connection>,
|
|
event_queue: EventQueue<WindowState>,
|
|
event_loop: EventLoop<'static, WindowState>,
|
|
}
|
|
|
|
impl WindowingSystem {
|
|
fn new(config: &WindowConfig) -> Result<Self> {
|
|
info!("Initializing WindowingSystem");
|
|
let connection =
|
|
Rc::new(Connection::connect_to_env().map_err(LayerShikaError::WaylandConnection)?);
|
|
let event_queue = connection.new_event_queue();
|
|
|
|
let (compositor, output, layer_shell, seat, fractional_scale_manager, viewporter) =
|
|
Self::initialize_globals(&connection, &event_queue.handle())
|
|
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
|
|
|
let (surface, layer_surface, fractional_scale, viewport) = Self::setup_surface(
|
|
&compositor,
|
|
&output,
|
|
&layer_shell,
|
|
fractional_scale_manager.as_ref(),
|
|
viewporter.as_ref(),
|
|
&event_queue.handle(),
|
|
config,
|
|
);
|
|
|
|
let pointer = Rc::new(seat.get_pointer(&event_queue.handle(), ()));
|
|
let window = Self::initialize_renderer(&surface, &connection.display(), config)
|
|
.map_err(|e| LayerShikaError::EGLContextCreation(e.to_string()))?;
|
|
|
|
let mut builder = WindowStateBuilder::new()
|
|
.with_component_definition(config.component_definition.clone())
|
|
.with_surface(Rc::clone(&surface))
|
|
.with_layer_surface(Rc::clone(&layer_surface))
|
|
.with_pointer(Rc::clone(&pointer))
|
|
.with_scale_factor(config.scale_factor)
|
|
.with_height(config.height)
|
|
.with_exclusive_zone(config.exclusive_zone)
|
|
.with_window(window);
|
|
|
|
if let Some(fs) = fractional_scale {
|
|
builder = builder.with_fractional_scale(fs);
|
|
}
|
|
|
|
if let Some(vp) = viewport {
|
|
builder = builder.with_viewport(vp);
|
|
}
|
|
|
|
let state = builder
|
|
.build()
|
|
.map_err(|e| LayerShikaError::WindowConfiguration(e.to_string()))?;
|
|
|
|
let event_loop =
|
|
EventLoop::try_new().map_err(|e| LayerShikaError::EventLoop(e.to_string()))?;
|
|
|
|
Ok(Self {
|
|
state,
|
|
connection,
|
|
event_queue,
|
|
event_loop,
|
|
})
|
|
}
|
|
|
|
fn initialize_globals(
|
|
connection: &Connection,
|
|
queue_handle: &QueueHandle<WindowState>,
|
|
) -> Result<GlobalObjects> {
|
|
let global_list = registry_queue_init::<WindowState>(connection)
|
|
.map(|(global_list, _)| global_list)
|
|
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
|
|
|
let (compositor, output, layer_shell, seat) = bind_globals!(
|
|
&global_list,
|
|
queue_handle,
|
|
(WlCompositor, compositor, 3..=6),
|
|
(WlOutput, output, 1..=4),
|
|
(ZwlrLayerShellV1, layer_shell, 1..=5),
|
|
(WlSeat, seat, 1..=9)
|
|
)?;
|
|
|
|
let fractional_scale_manager = global_list
|
|
.bind::<WpFractionalScaleManagerV1, _, _>(queue_handle, 1..=1, ())
|
|
.ok();
|
|
|
|
let viewporter = global_list
|
|
.bind::<WpViewporter, _, _>(queue_handle, 1..=1, ())
|
|
.ok();
|
|
|
|
if fractional_scale_manager.is_none() {
|
|
info!("Fractional scale protocol not available, using integer scaling");
|
|
}
|
|
|
|
if viewporter.is_none() {
|
|
info!("Viewporter protocol not available");
|
|
}
|
|
|
|
Ok((
|
|
compositor,
|
|
output,
|
|
layer_shell,
|
|
seat,
|
|
fractional_scale_manager,
|
|
viewporter,
|
|
))
|
|
}
|
|
|
|
fn setup_surface(
|
|
compositor: &WlCompositor,
|
|
output: &WlOutput,
|
|
layer_shell: &ZwlrLayerShellV1,
|
|
fractional_scale_manager: Option<&WpFractionalScaleManagerV1>,
|
|
viewporter: Option<&WpViewporter>,
|
|
queue_handle: &QueueHandle<WindowState>,
|
|
config: &WindowConfig,
|
|
) -> SurfaceObjects {
|
|
let surface = Rc::new(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 fractional_scale = fractional_scale_manager.map(|manager| {
|
|
info!("Creating fractional scale object for surface");
|
|
Rc::new(manager.get_fractional_scale(&surface, queue_handle, ()))
|
|
});
|
|
|
|
let viewport = viewporter.map(|vp| {
|
|
info!("Creating viewport for surface");
|
|
Rc::new(vp.get_viewport(&surface, queue_handle, ()))
|
|
});
|
|
|
|
Self::configure_layer_surface(&layer_surface, &surface, config);
|
|
|
|
surface.set_buffer_scale(1);
|
|
|
|
(surface, layer_surface, fractional_scale, viewport)
|
|
}
|
|
|
|
fn configure_layer_surface(
|
|
layer_surface: &Rc<ZwlrLayerSurfaceV1>,
|
|
surface: &WlSurface,
|
|
config: &WindowConfig,
|
|
) {
|
|
layer_surface.set_anchor(config.anchor);
|
|
layer_surface.set_margin(
|
|
config.margin.top,
|
|
config.margin.right,
|
|
config.margin.bottom,
|
|
config.margin.left,
|
|
);
|
|
|
|
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 initialize_renderer(
|
|
surface: &Rc<WlSurface>,
|
|
display: &WlDisplay,
|
|
config: &WindowConfig,
|
|
) -> Result<Rc<FemtoVGWindow>> {
|
|
let init_size = PhysicalSize::new(1, 1);
|
|
|
|
let context = EGLContext::builder()
|
|
.with_display_id(display.id())
|
|
.with_surface_id(surface.id())
|
|
.with_size(init_size)
|
|
.build()
|
|
.map_err(|e| LayerShikaError::EGLContextCreation(e.to_string()))?;
|
|
|
|
let renderer = FemtoVGRenderer::new(context)
|
|
.map_err(|e| LayerShikaError::FemtoVGRendererCreation(e.to_string()))?;
|
|
|
|
let femtovg_window = FemtoVGWindow::new(renderer);
|
|
femtovg_window.set_size(slint::WindowSize::Physical(init_size));
|
|
femtovg_window.set_scale_factor(config.scale_factor);
|
|
femtovg_window.set_position(LogicalPosition::new(0., 0.));
|
|
|
|
Ok(femtovg_window)
|
|
}
|
|
|
|
pub fn event_loop_handle(&self) -> LoopHandle<'static, WindowState> {
|
|
self.event_loop.handle()
|
|
}
|
|
|
|
pub fn run(&mut self) -> Result<()> {
|
|
info!("Starting WindowingSystem main loop");
|
|
|
|
while self
|
|
.event_queue
|
|
.blocking_dispatch(&mut self.state)
|
|
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?
|
|
> 0
|
|
{
|
|
self.connection
|
|
.flush()
|
|
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
|
self.state
|
|
.window()
|
|
.render_frame_if_dirty()
|
|
.map_err(|e| LayerShikaError::Rendering(e.to_string()))?;
|
|
}
|
|
|
|
self.setup_wayland_event_source()?;
|
|
|
|
let event_queue = &mut self.event_queue;
|
|
let connection = &self.connection;
|
|
|
|
self.event_loop
|
|
.run(None, &mut self.state, move |shared_data| {
|
|
if let Err(e) = Self::process_events(connection, event_queue, shared_data) {
|
|
error!("Error processing events: {e}");
|
|
}
|
|
})
|
|
.map_err(|e| LayerShikaError::EventLoop(e.to_string()))
|
|
}
|
|
|
|
fn setup_wayland_event_source(&self) -> Result<()> {
|
|
debug!("Setting up Wayland event source");
|
|
|
|
let connection = Rc::clone(&self.connection);
|
|
|
|
self.event_loop
|
|
.handle()
|
|
.insert_source(
|
|
Generic::new(connection, Interest::READ, Mode::Level),
|
|
move |_, _connection, _shared_data| Ok(PostAction::Continue),
|
|
)
|
|
.map_err(|e| LayerShikaError::EventLoop(e.to_string()))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn process_events(
|
|
connection: &Connection,
|
|
event_queue: &mut EventQueue<WindowState>,
|
|
shared_data: &mut WindowState,
|
|
) -> Result<()> {
|
|
if let Some(guard) = event_queue.prepare_read() {
|
|
guard
|
|
.read()
|
|
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
|
}
|
|
connection.flush()?;
|
|
|
|
event_queue
|
|
.dispatch_pending(shared_data)
|
|
.map_err(|e| LayerShikaError::WaylandProtocol(e.to_string()))?;
|
|
|
|
update_timers_and_animations();
|
|
|
|
shared_data
|
|
.window()
|
|
.render_frame_if_dirty()
|
|
.map_err(|e| LayerShikaError::Rendering(e.to_string()))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub const fn component_instance(&self) -> &ComponentInstance {
|
|
self.state.component_instance()
|
|
}
|
|
|
|
pub fn window(&self) -> Rc<FemtoVGWindow> {
|
|
self.state.window()
|
|
}
|
|
|
|
pub const fn state(&self) -> &WindowState {
|
|
&self.state
|
|
}
|
|
}
|