mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 11:25:54 +00:00
feat: bare multi-output support
This commit is contained in:
parent
f7cd653605
commit
84121a2162
16 changed files with 1026 additions and 197 deletions
|
|
@ -9,6 +9,7 @@ pub use rendering::femtovg::popup_window::PopupWindow;
|
|||
pub use wayland::config::WaylandWindowConfig;
|
||||
pub use wayland::facade::{PopupManagerFacade, RuntimeStateFacade, WindowingSystemFacade};
|
||||
pub use wayland::shell_adapter::WaylandWindowingSystem;
|
||||
pub use wayland::surfaces::app_state::AppState;
|
||||
pub use wayland::surfaces::popup_manager::PopupManager;
|
||||
pub use wayland::surfaces::surface_state::WindowState;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,29 +2,31 @@ use slint::{
|
|||
PlatformError,
|
||||
platform::{Platform, WindowAdapter},
|
||||
};
|
||||
use std::cell::{Cell, OnceCell};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::rendering::femtovg::main_window::FemtoVGWindow;
|
||||
|
||||
type PopupCreator = dyn Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError>;
|
||||
|
||||
pub struct CustomSlintPlatform {
|
||||
main_window: Weak<FemtoVGWindow>,
|
||||
pending_windows: RefCell<Vec<Rc<FemtoVGWindow>>>,
|
||||
popup_creator: OnceCell<Rc<PopupCreator>>,
|
||||
first_call: Cell<bool>,
|
||||
}
|
||||
|
||||
impl CustomSlintPlatform {
|
||||
#[must_use]
|
||||
pub fn new(window: &Rc<FemtoVGWindow>) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
main_window: Rc::downgrade(window),
|
||||
pending_windows: RefCell::new(vec![Rc::clone(window)]),
|
||||
popup_creator: OnceCell::new(),
|
||||
first_call: Cell::new(true),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_window(&self, window: Rc<FemtoVGWindow>) {
|
||||
self.pending_windows.borrow_mut().push(window);
|
||||
}
|
||||
|
||||
pub fn set_popup_creator<F>(&self, creator: F)
|
||||
where
|
||||
F: Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError> + 'static,
|
||||
|
|
@ -37,12 +39,10 @@ impl CustomSlintPlatform {
|
|||
|
||||
impl Platform for CustomSlintPlatform {
|
||||
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter + 'static>, PlatformError> {
|
||||
if self.first_call.get() {
|
||||
self.first_call.set(false);
|
||||
self.main_window
|
||||
.upgrade()
|
||||
.ok_or(PlatformError::NoPlatform)
|
||||
.map(|w| w as Rc<dyn WindowAdapter>)
|
||||
let mut windows = self.pending_windows.borrow_mut();
|
||||
if !windows.is_empty() {
|
||||
let window = windows.remove(0);
|
||||
Ok(window as Rc<dyn WindowAdapter>)
|
||||
} else if let Some(creator) = self.popup_creator.get() {
|
||||
creator()
|
||||
} else {
|
||||
|
|
|
|||
415
crates/adapters/src/wayland/event_handling/app_dispatcher.rs
Normal file
415
crates/adapters/src/wayland/event_handling/app_dispatcher.rs
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
use crate::wayland::surfaces::app_state::AppState;
|
||||
use crate::wayland::surfaces::display_metrics::DisplayMetrics;
|
||||
use log::info;
|
||||
use slint::PhysicalSize;
|
||||
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 wayland_client::WEnum;
|
||||
use wayland_client::{
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
globals::GlobalListContents,
|
||||
protocol::{
|
||||
wl_compositor::WlCompositor,
|
||||
wl_output::{self, WlOutput},
|
||||
wl_pointer::{self, WlPointer},
|
||||
wl_registry::WlRegistry,
|
||||
wl_seat::WlSeat,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
|
||||
};
|
||||
use wayland_protocols::wp::viewporter::client::{
|
||||
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||
};
|
||||
use wayland_protocols::xdg::shell::client::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::XdgPositioner,
|
||||
xdg_surface::{self, XdgSurface},
|
||||
xdg_wm_base::{self, XdgWmBase},
|
||||
};
|
||||
|
||||
impl Dispatch<ZwlrLayerSurfaceV1, ()> for AppState {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
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 compositor size: {width}x{height}");
|
||||
layer_surface.ack_configure(serial);
|
||||
|
||||
let layer_surface_id = layer_surface.id();
|
||||
let Some(window) = state.get_output_by_layer_surface_mut(&layer_surface_id) else {
|
||||
info!(
|
||||
"Could not find window for layer surface {:?}",
|
||||
layer_surface_id
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let output_width = window.output_size().width;
|
||||
let scale_factor = window.scale_factor();
|
||||
|
||||
let target_width = if width == 0 || (width == 1 && output_width > 1) {
|
||||
if scale_factor > 1.0 {
|
||||
(output_width as f32 / scale_factor).round() as u32
|
||||
} else {
|
||||
output_width
|
||||
}
|
||||
} else {
|
||||
width
|
||||
};
|
||||
|
||||
let target_height = if height > 0 {
|
||||
height
|
||||
} else {
|
||||
let h = window.height();
|
||||
if scale_factor > 1.0 {
|
||||
(h as f32 / scale_factor).round() as u32
|
||||
} else {
|
||||
h
|
||||
}
|
||||
};
|
||||
|
||||
let clamped_width = target_width.min(output_width);
|
||||
|
||||
info!(
|
||||
"Using dimensions: {}x{} (clamped from {}x{}, output: {}x{})",
|
||||
clamped_width,
|
||||
target_height,
|
||||
target_width,
|
||||
target_height,
|
||||
output_width,
|
||||
window.output_size().height
|
||||
);
|
||||
|
||||
window.update_size(clamped_width, target_height);
|
||||
}
|
||||
zwlr_layer_surface_v1::Event::Closed => {
|
||||
info!("Layer surface closed");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlOutput, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
proxy: &WlOutput,
|
||||
event: <WlOutput as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
wl_output::Event::Mode { width, height, .. } => {
|
||||
info!("WlOutput size changed to {width}x{height}");
|
||||
let width = width.try_into().unwrap_or_default();
|
||||
let height = height.try_into().unwrap_or_default();
|
||||
|
||||
let output_id = proxy.id();
|
||||
if let Some(window) = state.get_output_by_output_id_mut(&output_id) {
|
||||
window.set_output_size(PhysicalSize::new(width, height));
|
||||
}
|
||||
}
|
||||
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={x}, y={y}, physical_width={physical_width}, physical_height={physical_height}, subpixel={subpixel:?}, make={make:?}, model={model:?}, transform={transform:?}"
|
||||
);
|
||||
}
|
||||
wl_output::Event::Done => {
|
||||
info!("WlOutput done");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlPointer, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &WlPointer,
|
||||
event: <WlPointer as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
wl_pointer::Event::Enter {
|
||||
serial,
|
||||
surface,
|
||||
surface_x,
|
||||
surface_y,
|
||||
} => {
|
||||
info!("Pointer entered surface {:?}", surface.id());
|
||||
|
||||
let surface_id = surface.id();
|
||||
|
||||
if let Some(window) = state.get_output_by_surface_mut(&surface_id) {
|
||||
window.set_last_pointer_serial(serial);
|
||||
window.set_current_pointer_position(surface_x, surface_y);
|
||||
window.set_entered_surface(&surface);
|
||||
let position = window.current_pointer_position();
|
||||
window.dispatch_to_active_window(WindowEvent::PointerMoved { position });
|
||||
|
||||
if let Some(key) = state.get_key_by_surface(&surface_id).cloned() {
|
||||
state.set_active_output(Some(key));
|
||||
}
|
||||
} else {
|
||||
let key = state.get_key_by_popup(&surface_id);
|
||||
if let Some(window) = state.find_output_by_popup_mut(&surface_id) {
|
||||
window.set_last_pointer_serial(serial);
|
||||
window.set_current_pointer_position(surface_x, surface_y);
|
||||
window.set_entered_surface(&surface);
|
||||
let position = window.current_pointer_position();
|
||||
window.dispatch_to_active_window(WindowEvent::PointerMoved { position });
|
||||
|
||||
if let Some(key) = key {
|
||||
state.set_active_output(Some(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wl_pointer::Event::Motion {
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
if let Some(output_key) = state.active_output().cloned() {
|
||||
if let Some(window) = state.get_output_by_key_mut(&output_key) {
|
||||
window.set_current_pointer_position(surface_x, surface_y);
|
||||
let position = window.current_pointer_position();
|
||||
window.dispatch_to_active_window(WindowEvent::PointerMoved { position });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wl_pointer::Event::Leave { .. } => {
|
||||
if let Some(output_key) = state.active_output().cloned() {
|
||||
if let Some(window) = state.get_output_by_key_mut(&output_key) {
|
||||
window.dispatch_to_active_window(WindowEvent::PointerExited);
|
||||
window.clear_entered_surface();
|
||||
}
|
||||
}
|
||||
state.set_active_output(None);
|
||||
}
|
||||
|
||||
wl_pointer::Event::Button {
|
||||
serial,
|
||||
state: button_state,
|
||||
..
|
||||
} => {
|
||||
if let Some(output_key) = state.active_output().cloned() {
|
||||
if let Some(window) = state.get_output_by_key_mut(&output_key) {
|
||||
window.set_last_pointer_serial(serial);
|
||||
let position = window.current_pointer_position();
|
||||
let event = match button_state {
|
||||
WEnum::Value(wl_pointer::ButtonState::Pressed) => {
|
||||
WindowEvent::PointerPressed {
|
||||
button: PointerEventButton::Left,
|
||||
position,
|
||||
}
|
||||
}
|
||||
_ => WindowEvent::PointerReleased {
|
||||
button: PointerEventButton::Left,
|
||||
position,
|
||||
},
|
||||
};
|
||||
window.dispatch_to_active_window(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpFractionalScaleV1, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
proxy: &WpFractionalScaleV1,
|
||||
event: wp_fractional_scale_v1::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
|
||||
let scale_float = DisplayMetrics::scale_factor_from_120ths(scale);
|
||||
info!("Fractional scale received: {scale_float} ({scale}x)");
|
||||
|
||||
for window in state.all_outputs_mut() {
|
||||
window.update_scale_for_fractional_scale_object(proxy, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgWmBase, ()> for AppState {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
xdg_wm_base: &XdgWmBase,
|
||||
event: xdg_wm_base::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
if let xdg_wm_base::Event::Ping { serial } = event {
|
||||
info!("XdgWmBase ping received, sending pong with serial {serial}");
|
||||
xdg_wm_base.pong(serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPopup, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
xdg_popup: &XdgPopup,
|
||||
event: xdg_popup::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
xdg_popup::Event::Configure {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})");
|
||||
|
||||
let popup_id = xdg_popup.id();
|
||||
for window in state.all_outputs_mut() {
|
||||
if let Some(popup_manager) = window.popup_manager() {
|
||||
if let Some(handle) = popup_manager.find_by_xdg_popup(&popup_id) {
|
||||
info!(
|
||||
"Marking popup with handle {handle:?} as configured after XdgPopup::Configure"
|
||||
);
|
||||
popup_manager.mark_popup_configured(handle.key());
|
||||
popup_manager.mark_all_popups_dirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
xdg_popup::Event::PopupDone => {
|
||||
info!("XdgPopup dismissed by compositor");
|
||||
let popup_id = xdg_popup.id();
|
||||
|
||||
for window in state.all_outputs_mut() {
|
||||
let popup_handle = window
|
||||
.popup_manager()
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.find_by_xdg_popup(&popup_id));
|
||||
|
||||
if let Some(handle) = popup_handle {
|
||||
info!("Destroying popup with handle {handle:?}");
|
||||
if let Some(popup_manager) = window.popup_manager() {
|
||||
let _result = popup_manager.close(handle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
xdg_popup::Event::Repositioned { token } => {
|
||||
info!("XdgPopup repositioned with token {token}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgSurface, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
xdg_surface: &XdgSurface,
|
||||
event: xdg_surface::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
if let xdg_surface::Event::Configure { serial } = event {
|
||||
info!("XdgSurface Configure received, sending ack with serial {serial}");
|
||||
xdg_surface.ack_configure(serial);
|
||||
|
||||
let xdg_surface_id = xdg_surface.id();
|
||||
for window in state.all_outputs_mut() {
|
||||
if let Some(popup_manager) = window.popup_manager() {
|
||||
if popup_manager.find_by_xdg_surface(&xdg_surface_id).is_some() {
|
||||
info!("Marking all popups as dirty after Configure");
|
||||
popup_manager.mark_all_popups_dirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_empty_dispatch_app {
|
||||
($(($t:ty, $u:ty)),+) => {
|
||||
$(
|
||||
impl Dispatch<$t, $u> for AppState {
|
||||
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));
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_empty_dispatch_app!(
|
||||
(WlRegistry, GlobalListContents),
|
||||
(WlCompositor, ()),
|
||||
(WlSurface, ()),
|
||||
(ZwlrLayerShellV1, ()),
|
||||
(WlSeat, ()),
|
||||
(WpFractionalScaleManagerV1, ()),
|
||||
(WpViewporter, ()),
|
||||
(WpViewport, ()),
|
||||
(XdgPositioner, ())
|
||||
);
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod app_dispatcher;
|
||||
pub mod event_dispatcher;
|
||||
pub mod event_macros;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl WindowingSystemFacade {
|
|||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn component_instance(&self) -> &ComponentInstance {
|
||||
pub fn component_instance(&self) -> Result<&ComponentInstance> {
|
||||
self.inner.component_instance()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_man
|
|||
use wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||
use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase;
|
||||
|
||||
use crate::wayland::surfaces::surface_state::WindowState;
|
||||
use crate::wayland::surfaces::app_state::AppState;
|
||||
|
||||
pub struct GlobalContext {
|
||||
pub compositor: WlCompositor,
|
||||
pub output: WlOutput,
|
||||
pub outputs: Vec<WlOutput>,
|
||||
pub layer_shell: ZwlrLayerShellV1,
|
||||
pub seat: WlSeat,
|
||||
pub xdg_wm_base: Option<XdgWmBase>,
|
||||
|
|
@ -25,21 +25,57 @@ pub struct GlobalContext {
|
|||
impl GlobalContext {
|
||||
pub fn initialize(
|
||||
connection: &Connection,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
queue_handle: &QueueHandle<AppState>,
|
||||
) -> Result<Self, LayerShikaError> {
|
||||
let global_list = registry_queue_init::<WindowState>(connection)
|
||||
let global_list = registry_queue_init::<AppState>(connection)
|
||||
.map(|(global_list, _)| global_list)
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization { source: e })?;
|
||||
|
||||
let (compositor, output, layer_shell, seat) = bind_globals!(
|
||||
let (compositor, 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 output_names: Vec<u32> = global_list
|
||||
.contents()
|
||||
.clone_list()
|
||||
.into_iter()
|
||||
.filter(|global| global.interface == "wl_output")
|
||||
.map(|global| {
|
||||
info!(
|
||||
"Found wl_output global with name: {} at version {}",
|
||||
global.name, global.version
|
||||
);
|
||||
global.name
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!(
|
||||
"Total unique wl_output globals found: {}",
|
||||
output_names.len()
|
||||
);
|
||||
|
||||
let outputs: Vec<WlOutput> = output_names
|
||||
.iter()
|
||||
.map(|&name| {
|
||||
info!("Binding wl_output with name: {}", name);
|
||||
global_list
|
||||
.registry()
|
||||
.bind::<WlOutput, _, _>(name, 4, queue_handle, ())
|
||||
})
|
||||
.collect();
|
||||
|
||||
if outputs.is_empty() {
|
||||
return Err(LayerShikaError::InvalidInput {
|
||||
message: "No outputs found".into(),
|
||||
});
|
||||
}
|
||||
|
||||
info!("Discovered {} output(s)", outputs.len());
|
||||
|
||||
let xdg_wm_base = global_list
|
||||
.bind::<XdgWmBase, _, _>(queue_handle, 1..=6, ())
|
||||
.ok();
|
||||
|
|
@ -66,7 +102,7 @@ impl GlobalContext {
|
|||
|
||||
Ok(Self {
|
||||
compositor,
|
||||
output,
|
||||
outputs,
|
||||
layer_shell,
|
||||
seat,
|
||||
xdg_wm_base,
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ pub(crate) mod event_handling;
|
|||
pub(crate) mod facade;
|
||||
pub(crate) mod globals;
|
||||
pub(crate) mod managed_proxies;
|
||||
pub(crate) mod outputs;
|
||||
pub(crate) mod shell_adapter;
|
||||
pub(crate) mod surfaces;
|
||||
|
|
|
|||
20
crates/adapters/src/wayland/outputs.rs
Normal file
20
crates/adapters/src/wayland/outputs.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use wayland_client::backend::ObjectId;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct OutputKey(ObjectId);
|
||||
|
||||
impl OutputKey {
|
||||
pub const fn new(id: ObjectId) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
pub const fn id(&self) -> &ObjectId {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectId> for OutputKey {
|
||||
fn from(id: ObjectId) -> Self {
|
||||
Self::new(id)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
use crate::wayland::{
|
||||
config::{LayerSurfaceConfig, WaylandWindowConfig},
|
||||
globals::context::GlobalContext,
|
||||
managed_proxies::ManagedWlPointer,
|
||||
surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
|
||||
surfaces::popup_manager::{PopupContext, PopupManager},
|
||||
surfaces::{
|
||||
event_context::SharedPointerSerial, surface_builder::WindowStateBuilder,
|
||||
app_state::AppState,
|
||||
event_context::SharedPointerSerial,
|
||||
surface_builder::{PlatformWrapper, WindowStateBuilder},
|
||||
surface_state::WindowState,
|
||||
},
|
||||
};
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||
use crate::{
|
||||
errors::{EventLoopError, LayerShikaError, RenderingError, Result},
|
||||
rendering::{
|
||||
|
|
@ -22,7 +26,7 @@ use layer_shika_domain::ports::windowing::WindowingSystemPort;
|
|||
use log::{error, info};
|
||||
use slint::{
|
||||
LogicalPosition, PhysicalSize, PlatformError, WindowPosition,
|
||||
platform::{WindowAdapter, femtovg_renderer::FemtoVGRenderer, update_timers_and_animations},
|
||||
platform::{WindowAdapter, femtovg_renderer::FemtoVGRenderer, set_platform, update_timers_and_animations},
|
||||
};
|
||||
use slint_interpreter::ComponentInstance;
|
||||
use smithay_client_toolkit::reexports::calloop::{
|
||||
|
|
@ -30,90 +34,76 @@ use smithay_client_toolkit::reexports::calloop::{
|
|||
};
|
||||
use std::rc::Rc;
|
||||
use wayland_client::{
|
||||
Connection, EventQueue, Proxy,
|
||||
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
||||
Connection, EventQueue, Proxy, QueueHandle,
|
||||
backend::ObjectId,
|
||||
protocol::{wl_display::WlDisplay, wl_pointer::WlPointer, wl_surface::WlSurface},
|
||||
};
|
||||
|
||||
type PopupManagersAndSurfaces = (Vec<Rc<PopupManager>>, Vec<Rc<ZwlrLayerSurfaceV1>>);
|
||||
|
||||
struct OutputSetup {
|
||||
output_id: ObjectId,
|
||||
main_surface_id: ObjectId,
|
||||
window: Rc<FemtoVGWindow>,
|
||||
builder: WindowStateBuilder,
|
||||
}
|
||||
|
||||
pub struct WaylandWindowingSystem {
|
||||
state: WindowState,
|
||||
state: AppState,
|
||||
connection: Rc<Connection>,
|
||||
event_queue: EventQueue<WindowState>,
|
||||
event_loop: EventLoop<'static, WindowState>,
|
||||
popup_manager: Rc<PopupManager>,
|
||||
event_queue: EventQueue<AppState>,
|
||||
event_loop: EventLoop<'static, AppState>,
|
||||
}
|
||||
|
||||
impl WaylandWindowingSystem {
|
||||
pub fn new(config: WaylandWindowConfig) -> Result<Self> {
|
||||
pub fn new(config: &WaylandWindowConfig) -> Result<Self> {
|
||||
info!("Initializing WindowingSystem");
|
||||
let (connection, event_queue) = Self::init_wayland_connection()?;
|
||||
let (state, global_ctx, platform) = Self::init_state(config, &connection, &event_queue)?;
|
||||
let (connection, mut event_queue) = Self::init_wayland_connection()?;
|
||||
let event_loop =
|
||||
EventLoop::try_new().map_err(|e| EventLoopError::Creation { source: e })?;
|
||||
|
||||
let popup_context = PopupContext::new(
|
||||
global_ctx.compositor,
|
||||
global_ctx.xdg_wm_base,
|
||||
global_ctx.seat,
|
||||
global_ctx.fractional_scale_manager,
|
||||
global_ctx.viewporter,
|
||||
connection.display(),
|
||||
Rc::clone(&connection),
|
||||
);
|
||||
|
||||
let popup_manager = Rc::new(PopupManager::new(
|
||||
popup_context,
|
||||
Rc::clone(state.display_metrics()),
|
||||
));
|
||||
let shared_serial = Rc::new(SharedPointerSerial::new());
|
||||
|
||||
Self::setup_popup_creator(
|
||||
&popup_manager,
|
||||
&platform,
|
||||
&state,
|
||||
&event_queue,
|
||||
&shared_serial,
|
||||
);
|
||||
let state = Self::init_state(config, &connection, &mut event_queue)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
connection,
|
||||
event_queue,
|
||||
event_loop,
|
||||
popup_manager,
|
||||
})
|
||||
.map(|mut system| {
|
||||
system
|
||||
.state
|
||||
.set_popup_manager(Rc::clone(&system.popup_manager));
|
||||
system.state.set_shared_pointer_serial(shared_serial);
|
||||
system
|
||||
})
|
||||
}
|
||||
|
||||
fn init_wayland_connection() -> Result<(Rc<Connection>, EventQueue<WindowState>)> {
|
||||
fn init_wayland_connection() -> Result<(Rc<Connection>, EventQueue<AppState>)> {
|
||||
let connection = Rc::new(Connection::connect_to_env()?);
|
||||
let event_queue = connection.new_event_queue();
|
||||
Ok((connection, event_queue))
|
||||
}
|
||||
|
||||
fn init_state(
|
||||
config: WaylandWindowConfig,
|
||||
connection: &Connection,
|
||||
event_queue: &EventQueue<WindowState>,
|
||||
) -> Result<(WindowState, GlobalContext, Rc<CustomSlintPlatform>)> {
|
||||
let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())?;
|
||||
|
||||
let layer_surface_config = LayerSurfaceConfig {
|
||||
fn create_layer_surface_config(config: &WaylandWindowConfig) -> LayerSurfaceConfig {
|
||||
LayerSurfaceConfig {
|
||||
anchor: config.anchor,
|
||||
margin: config.margin,
|
||||
exclusive_zone: config.exclusive_zone,
|
||||
keyboard_interactivity: config.keyboard_interactivity,
|
||||
height: config.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn create_output_setups(
|
||||
config: &WaylandWindowConfig,
|
||||
global_ctx: &GlobalContext,
|
||||
connection: &Connection,
|
||||
event_queue: &mut EventQueue<AppState>,
|
||||
pointer: &Rc<WlPointer>,
|
||||
layer_surface_config: &LayerSurfaceConfig,
|
||||
) -> Result<Vec<OutputSetup>> {
|
||||
let mut setups = Vec::new();
|
||||
|
||||
for output in &global_ctx.outputs {
|
||||
let output_id = output.id();
|
||||
|
||||
let setup_params = SurfaceSetupParams {
|
||||
compositor: &global_ctx.compositor,
|
||||
output: &global_ctx.output,
|
||||
output,
|
||||
layer_shell: &global_ctx.layer_shell,
|
||||
fractional_scale_manager: global_ctx.fractional_scale_manager.as_ref(),
|
||||
viewporter: global_ctx.viewporter.as_ref(),
|
||||
|
|
@ -122,24 +112,23 @@ impl WaylandWindowingSystem {
|
|||
namespace: config.namespace.clone(),
|
||||
};
|
||||
|
||||
let surface_ctx = SurfaceCtx::setup(&setup_params, &layer_surface_config);
|
||||
let surface_ctx = SurfaceCtx::setup(&setup_params, layer_surface_config);
|
||||
let main_surface_id = surface_ctx.surface.id();
|
||||
|
||||
let window =
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), &config)?;
|
||||
|
||||
let pointer = Rc::new(global_ctx.seat.get_pointer(&event_queue.handle(), ()));
|
||||
Self::initialize_renderer(&surface_ctx.surface, &connection.display(), config)?;
|
||||
|
||||
let mut builder = WindowStateBuilder::new()
|
||||
.with_component_definition(config.component_definition)
|
||||
.with_compilation_result(config.compilation_result)
|
||||
.with_component_definition(config.component_definition.clone())
|
||||
.with_compilation_result(config.compilation_result.clone())
|
||||
.with_surface(Rc::clone(&surface_ctx.surface))
|
||||
.with_layer_surface(Rc::clone(&surface_ctx.layer_surface))
|
||||
.with_scale_factor(config.scale_factor)
|
||||
.with_height(config.height)
|
||||
.with_exclusive_zone(config.exclusive_zone)
|
||||
.with_connection(Rc::new(connection.clone()))
|
||||
.with_pointer(Rc::clone(&pointer))
|
||||
.with_window(window);
|
||||
.with_pointer(Rc::clone(pointer))
|
||||
.with_window(Rc::clone(&window));
|
||||
|
||||
if let Some(fs) = &surface_ctx.fractional_scale {
|
||||
builder = builder.with_fractional_scale(Rc::clone(fs));
|
||||
|
|
@ -149,52 +138,170 @@ impl WaylandWindowingSystem {
|
|||
builder = builder.with_viewport(Rc::clone(vp));
|
||||
}
|
||||
|
||||
let (state, platform) =
|
||||
builder
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
Ok((state, global_ctx, platform))
|
||||
setups.push(OutputSetup {
|
||||
output_id,
|
||||
main_surface_id,
|
||||
window,
|
||||
builder,
|
||||
});
|
||||
}
|
||||
|
||||
fn setup_popup_creator(
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
Ok(setups)
|
||||
}
|
||||
|
||||
fn setup_platform(setups: &[OutputSetup]) -> Result<Rc<CustomSlintPlatform>> {
|
||||
let first_setup = setups
|
||||
.first()
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "No outputs available".into(),
|
||||
})?;
|
||||
|
||||
let platform = CustomSlintPlatform::new(&first_setup.window);
|
||||
|
||||
for setup in setups.iter().skip(1) {
|
||||
platform.add_window(Rc::clone(&setup.window));
|
||||
}
|
||||
|
||||
set_platform(Box::new(PlatformWrapper(Rc::clone(&platform))))
|
||||
.map_err(|e| LayerShikaError::PlatformSetup { source: e })?;
|
||||
|
||||
Ok(platform)
|
||||
}
|
||||
|
||||
fn create_window_states(
|
||||
setups: Vec<OutputSetup>,
|
||||
popup_context: &PopupContext,
|
||||
shared_serial: &Rc<SharedPointerSerial>,
|
||||
app_state: &mut AppState,
|
||||
) -> Result<PopupManagersAndSurfaces> {
|
||||
let mut popup_managers = Vec::new();
|
||||
let mut layer_surfaces = Vec::new();
|
||||
|
||||
for setup in setups {
|
||||
let mut per_output_window = WindowState::new(setup.builder).map_err(|e| {
|
||||
LayerShikaError::WindowConfiguration {
|
||||
message: e.to_string(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let popup_manager = Rc::new(PopupManager::new(
|
||||
popup_context.clone(),
|
||||
Rc::clone(per_output_window.display_metrics()),
|
||||
));
|
||||
|
||||
per_output_window.set_popup_manager(Rc::clone(&popup_manager));
|
||||
per_output_window.set_shared_pointer_serial(Rc::clone(shared_serial));
|
||||
|
||||
popup_managers.push(Rc::clone(&popup_manager));
|
||||
layer_surfaces.push(per_output_window.layer_surface());
|
||||
|
||||
app_state.add_output(setup.output_id, setup.main_surface_id, per_output_window);
|
||||
}
|
||||
|
||||
Ok((popup_managers, layer_surfaces))
|
||||
}
|
||||
|
||||
fn init_state(
|
||||
config: &WaylandWindowConfig,
|
||||
connection: &Connection,
|
||||
event_queue: &mut EventQueue<AppState>,
|
||||
) -> Result<AppState> {
|
||||
let global_ctx = GlobalContext::initialize(connection, &event_queue.handle())?;
|
||||
let layer_surface_config = Self::create_layer_surface_config(config);
|
||||
|
||||
let pointer = Rc::new(global_ctx.seat.get_pointer(&event_queue.handle(), ()));
|
||||
let shared_serial = Rc::new(SharedPointerSerial::new());
|
||||
|
||||
let mut app_state = AppState::new(
|
||||
ManagedWlPointer::new(Rc::clone(&pointer), Rc::new(connection.clone())),
|
||||
Rc::clone(&shared_serial),
|
||||
);
|
||||
|
||||
let popup_context = PopupContext::new(
|
||||
global_ctx.compositor.clone(),
|
||||
global_ctx.xdg_wm_base.clone(),
|
||||
global_ctx.seat.clone(),
|
||||
global_ctx.fractional_scale_manager.clone(),
|
||||
global_ctx.viewporter.clone(),
|
||||
connection.display(),
|
||||
Rc::new(connection.clone()),
|
||||
);
|
||||
|
||||
let setups = Self::create_output_setups(
|
||||
config,
|
||||
&global_ctx,
|
||||
connection,
|
||||
event_queue,
|
||||
&pointer,
|
||||
&layer_surface_config,
|
||||
)?;
|
||||
|
||||
let platform = Self::setup_platform(&setups)?;
|
||||
|
||||
let (popup_managers, layer_surfaces) =
|
||||
Self::create_window_states(setups, &popup_context, &shared_serial, &mut app_state)?;
|
||||
|
||||
Self::setup_shared_popup_creator(
|
||||
popup_managers,
|
||||
layer_surfaces,
|
||||
&platform,
|
||||
&event_queue.handle(),
|
||||
&shared_serial,
|
||||
);
|
||||
|
||||
Ok(app_state)
|
||||
}
|
||||
|
||||
fn setup_shared_popup_creator(
|
||||
popup_managers: Vec<Rc<PopupManager>>,
|
||||
layer_surfaces: Vec<Rc<ZwlrLayerSurfaceV1>>,
|
||||
platform: &Rc<CustomSlintPlatform>,
|
||||
state: &WindowState,
|
||||
event_queue: &EventQueue<WindowState>,
|
||||
queue_handle: &QueueHandle<AppState>,
|
||||
shared_serial: &Rc<SharedPointerSerial>,
|
||||
) {
|
||||
if !popup_manager.has_xdg_shell() {
|
||||
let Some(first_manager) = popup_managers.first() else {
|
||||
info!("No popup managers available");
|
||||
return;
|
||||
};
|
||||
|
||||
if !first_manager.has_xdg_shell() {
|
||||
info!("xdg-shell not available, popups will not be supported");
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Setting up popup creator with xdg-shell support");
|
||||
info!(
|
||||
"Setting up shared popup creator for {} output(s)",
|
||||
popup_managers.len()
|
||||
);
|
||||
|
||||
let popup_manager_clone = Rc::clone(popup_manager);
|
||||
let layer_surface = state.layer_surface();
|
||||
let queue_handle = event_queue.handle();
|
||||
let queue_handle_clone = queue_handle.clone();
|
||||
let serial_holder = Rc::clone(shared_serial);
|
||||
|
||||
platform.set_popup_creator(move || {
|
||||
info!("Popup creator called! Creating popup window...");
|
||||
info!("Popup creator called! Searching for pending popup...");
|
||||
|
||||
let serial = serial_holder.get();
|
||||
|
||||
let popup_window = popup_manager_clone
|
||||
.create_pending_popup(&queue_handle, &layer_surface, serial)
|
||||
.map_err(|e| PlatformError::Other(format!("Failed to create popup: {e}")))?;
|
||||
for (idx, (popup_manager, layer_surface)) in
|
||||
popup_managers.iter().zip(layer_surfaces.iter()).enumerate()
|
||||
{
|
||||
if popup_manager.has_pending_popup() {
|
||||
info!("Found pending popup in output #{}", idx);
|
||||
|
||||
let result = Ok(popup_window as Rc<dyn WindowAdapter>);
|
||||
let popup_window = popup_manager
|
||||
.create_pending_popup(&queue_handle_clone, layer_surface, serial)
|
||||
.map_err(|e| {
|
||||
PlatformError::Other(format!("Failed to create popup: {e}"))
|
||||
})?;
|
||||
|
||||
match &result {
|
||||
Ok(_) => info!("Popup created successfully"),
|
||||
Err(e) => info!("Popup creation failed: {e:?}"),
|
||||
info!("Popup created successfully for output #{}", idx);
|
||||
return Ok(popup_window as Rc<dyn WindowAdapter>);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
Err(PlatformError::Other(
|
||||
"No pending popup request found in any output".into(),
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +329,7 @@ impl WaylandWindowingSystem {
|
|||
Ok(femtovg_window)
|
||||
}
|
||||
|
||||
pub fn event_loop_handle(&self) -> LoopHandle<'static, WindowState> {
|
||||
pub fn event_loop_handle(&self) -> LoopHandle<'static, AppState> {
|
||||
self.event_loop.handle()
|
||||
}
|
||||
|
||||
|
|
@ -236,25 +343,25 @@ impl WaylandWindowingSystem {
|
|||
.map_err(|e| LayerShikaError::WaylandProtocol { source: e })?;
|
||||
|
||||
update_timers_and_animations();
|
||||
self.state
|
||||
|
||||
for window in self.state.all_outputs() {
|
||||
window
|
||||
.window()
|
||||
.render_frame_if_dirty()
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
self.setup_wayland_event_source()?;
|
||||
|
||||
let event_queue = &mut self.event_queue;
|
||||
let connection = &self.connection;
|
||||
let popup_manager = Rc::clone(&self.popup_manager);
|
||||
|
||||
self.event_loop
|
||||
.run(None, &mut self.state, move |shared_data| {
|
||||
if let Err(e) =
|
||||
Self::process_events(connection, event_queue, shared_data, &popup_manager)
|
||||
{
|
||||
if let Err(e) = Self::process_events(connection, event_queue, shared_data) {
|
||||
error!("Error processing events: {e}");
|
||||
}
|
||||
})
|
||||
|
|
@ -281,9 +388,8 @@ impl WaylandWindowingSystem {
|
|||
|
||||
fn process_events(
|
||||
connection: &Connection,
|
||||
event_queue: &mut EventQueue<WindowState>,
|
||||
shared_data: &mut WindowState,
|
||||
popup_manager: &PopupManager,
|
||||
event_queue: &mut EventQueue<AppState>,
|
||||
shared_data: &mut AppState,
|
||||
) -> Result<()> {
|
||||
if let Some(guard) = event_queue.prepare_read() {
|
||||
guard
|
||||
|
|
@ -295,18 +401,22 @@ impl WaylandWindowingSystem {
|
|||
|
||||
update_timers_and_animations();
|
||||
|
||||
shared_data
|
||||
for window in shared_data.all_outputs() {
|
||||
window
|
||||
.window()
|
||||
.render_frame_if_dirty()
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
if let Some(popup_manager) = window.popup_manager() {
|
||||
popup_manager
|
||||
.render_popups()
|
||||
.map_err(|e| RenderingError::Operation {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
connection
|
||||
.flush()
|
||||
|
|
@ -315,11 +425,24 @@ impl WaylandWindowingSystem {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub const fn component_instance(&self) -> &ComponentInstance {
|
||||
self.state.component_instance()
|
||||
pub fn component_instance(&self) -> Result<&ComponentInstance> {
|
||||
self.state
|
||||
.primary_output()
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "No outputs available".into(),
|
||||
})
|
||||
.map(WindowState::component_instance)
|
||||
}
|
||||
|
||||
pub const fn state(&self) -> &WindowState {
|
||||
pub fn state(&self) -> Result<&WindowState> {
|
||||
self.state
|
||||
.primary_output()
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput {
|
||||
message: "No outputs available".into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn app_state(&self) -> &AppState {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
|
|
|||
161
crates/adapters/src/wayland/surfaces/app_state.rs
Normal file
161
crates/adapters/src/wayland/surfaces/app_state.rs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
use super::surface_state::WindowState;
|
||||
use super::event_context::SharedPointerSerial;
|
||||
use crate::wayland::managed_proxies::ManagedWlPointer;
|
||||
use crate::wayland::outputs::OutputKey;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use wayland_client::backend::ObjectId;
|
||||
use wayland_client::Proxy;
|
||||
|
||||
pub type PerOutputWindow = WindowState;
|
||||
|
||||
pub struct AppState {
|
||||
outputs: HashMap<OutputKey, PerOutputWindow>,
|
||||
surface_to_output: HashMap<ObjectId, OutputKey>,
|
||||
output_to_key: HashMap<ObjectId, OutputKey>,
|
||||
_pointer: ManagedWlPointer,
|
||||
shared_pointer_serial: Rc<SharedPointerSerial>,
|
||||
active_output: Option<OutputKey>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(pointer: ManagedWlPointer, shared_serial: Rc<SharedPointerSerial>) -> Self {
|
||||
Self {
|
||||
outputs: HashMap::new(),
|
||||
surface_to_output: HashMap::new(),
|
||||
output_to_key: HashMap::new(),
|
||||
_pointer: pointer,
|
||||
shared_pointer_serial: shared_serial,
|
||||
active_output: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_output(
|
||||
&mut self,
|
||||
output_id: ObjectId,
|
||||
main_surface_id: ObjectId,
|
||||
window: PerOutputWindow,
|
||||
) {
|
||||
let key = OutputKey::new(output_id.clone());
|
||||
self.output_to_key.insert(output_id, key.clone());
|
||||
self.surface_to_output
|
||||
.insert(main_surface_id, key.clone());
|
||||
self.outputs.insert(key, window);
|
||||
}
|
||||
|
||||
pub fn get_output_by_key(&self, key: &OutputKey) -> Option<&PerOutputWindow> {
|
||||
self.outputs.get(key)
|
||||
}
|
||||
|
||||
pub fn get_output_by_key_mut(&mut self, key: &OutputKey) -> Option<&mut PerOutputWindow> {
|
||||
self.outputs.get_mut(key)
|
||||
}
|
||||
|
||||
pub fn get_output_by_output_id(&self, output_id: &ObjectId) -> Option<&PerOutputWindow> {
|
||||
self.output_to_key
|
||||
.get(output_id)
|
||||
.and_then(|key| self.outputs.get(key))
|
||||
}
|
||||
|
||||
pub fn get_output_by_output_id_mut(
|
||||
&mut self,
|
||||
output_id: &ObjectId,
|
||||
) -> Option<&mut PerOutputWindow> {
|
||||
self.output_to_key
|
||||
.get(output_id)
|
||||
.and_then(|key| self.outputs.get_mut(key))
|
||||
}
|
||||
|
||||
pub fn get_output_by_surface(&self, surface_id: &ObjectId) -> Option<&PerOutputWindow> {
|
||||
self.surface_to_output
|
||||
.get(surface_id)
|
||||
.and_then(|key| self.outputs.get(key))
|
||||
}
|
||||
|
||||
pub fn get_output_by_surface_mut(
|
||||
&mut self,
|
||||
surface_id: &ObjectId,
|
||||
) -> Option<&mut PerOutputWindow> {
|
||||
self.surface_to_output
|
||||
.get(surface_id)
|
||||
.and_then(|key| self.outputs.get_mut(key))
|
||||
}
|
||||
|
||||
pub fn get_output_by_layer_surface_mut(
|
||||
&mut self,
|
||||
layer_surface_id: &ObjectId,
|
||||
) -> Option<&mut PerOutputWindow> {
|
||||
self.outputs.values_mut().find(|window| {
|
||||
window.layer_surface().as_ref().id() == *layer_surface_id
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_key_by_surface(&self, surface_id: &ObjectId) -> Option<&OutputKey> {
|
||||
self.surface_to_output.get(surface_id)
|
||||
}
|
||||
|
||||
pub fn get_key_by_output_id(&self, output_id: &ObjectId) -> Option<&OutputKey> {
|
||||
self.output_to_key.get(output_id)
|
||||
}
|
||||
|
||||
pub fn register_popup_surface(&mut self, popup_surface_id: ObjectId, output_key: OutputKey) {
|
||||
self.surface_to_output.insert(popup_surface_id, output_key);
|
||||
}
|
||||
|
||||
pub fn set_active_output(&mut self, key: Option<OutputKey>) {
|
||||
self.active_output = key;
|
||||
}
|
||||
|
||||
pub const fn active_output(&self) -> Option<&OutputKey> {
|
||||
self.active_output.as_ref()
|
||||
}
|
||||
|
||||
pub fn primary_output(&self) -> Option<&PerOutputWindow> {
|
||||
self.outputs.values().next()
|
||||
}
|
||||
|
||||
pub fn all_outputs(&self) -> impl Iterator<Item = &PerOutputWindow> {
|
||||
self.outputs.values()
|
||||
}
|
||||
|
||||
pub fn all_outputs_mut(&mut self) -> impl Iterator<Item = &mut PerOutputWindow> {
|
||||
self.outputs.values_mut()
|
||||
}
|
||||
|
||||
pub const fn shared_pointer_serial(&self) -> &Rc<SharedPointerSerial> {
|
||||
&self.shared_pointer_serial
|
||||
}
|
||||
|
||||
pub fn find_output_by_popup(&self, popup_surface_id: &ObjectId) -> Option<&PerOutputWindow> {
|
||||
self.outputs.values().find(|window| {
|
||||
window
|
||||
.popup_manager()
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.find_by_surface(popup_surface_id))
|
||||
.is_some()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_output_by_popup_mut(
|
||||
&mut self,
|
||||
popup_surface_id: &ObjectId,
|
||||
) -> Option<&mut PerOutputWindow> {
|
||||
self.outputs.values_mut().find(|window| {
|
||||
window
|
||||
.popup_manager()
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.find_by_surface(popup_surface_id))
|
||||
.is_some()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_key_by_popup(&self, popup_surface_id: &ObjectId) -> Option<OutputKey> {
|
||||
self.outputs.iter().find_map(|(key, window)| {
|
||||
window
|
||||
.popup_manager()
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.find_by_surface(popup_surface_id))
|
||||
.map(|_| key.clone())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::wayland::{config::LayerSurfaceConfig, surfaces::surface_state::WindowState};
|
||||
use crate::wayland::{config::LayerSurfaceConfig, surfaces::app_state::AppState};
|
||||
use log::info;
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{
|
||||
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
|
||||
|
|
@ -23,7 +23,7 @@ pub struct SurfaceSetupParams<'a> {
|
|||
pub layer_shell: &'a ZwlrLayerShellV1,
|
||||
pub fractional_scale_manager: Option<&'a WpFractionalScaleManagerV1>,
|
||||
pub viewporter: Option<&'a WpViewporter>,
|
||||
pub queue_handle: &'a QueueHandle<WindowState>,
|
||||
pub queue_handle: &'a QueueHandle<AppState>,
|
||||
pub layer: Layer,
|
||||
pub namespace: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod app_state;
|
||||
pub mod component_state;
|
||||
pub mod dimensions;
|
||||
pub mod display_metrics;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1:
|
|||
use wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||
use wayland_protocols::xdg::shell::client::xdg_wm_base::XdgWmBase;
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::popup_surface::PopupSurface;
|
||||
use super::surface_state::WindowState;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ActiveWindow {
|
||||
|
|
@ -66,6 +66,7 @@ pub struct CreatePopupParams {
|
|||
pub grab: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PopupContext {
|
||||
compositor: WlCompositor,
|
||||
xdg_wm_base: Option<XdgWmBase>,
|
||||
|
|
@ -184,6 +185,11 @@ impl PopupManager {
|
|||
.map(|p| (p.id, p.request, p.width, p.height))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn has_pending_popup(&self) -> bool {
|
||||
self.state.borrow().pending_popup.is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn scale_factor(&self) -> f32 {
|
||||
self.scale_factor.get()
|
||||
|
|
@ -225,7 +231,7 @@ impl PopupManager {
|
|||
|
||||
pub fn create_pending_popup(
|
||||
self: &Rc<Self>,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
queue_handle: &QueueHandle<AppState>,
|
||||
parent_layer_surface: &ZwlrLayerSurfaceV1,
|
||||
last_pointer_serial: u32,
|
||||
) -> Result<Rc<PopupWindow>> {
|
||||
|
|
@ -250,7 +256,7 @@ impl PopupManager {
|
|||
|
||||
fn create_popup_internal(
|
||||
self: &Rc<Self>,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
queue_handle: &QueueHandle<AppState>,
|
||||
parent_layer_surface: &ZwlrLayerSurfaceV1,
|
||||
params: CreatePopupParams,
|
||||
request: PopupRequest,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use wayland_protocols::xdg::shell::client::{
|
|||
xdg_wm_base::XdgWmBase,
|
||||
};
|
||||
|
||||
use super::surface_state::WindowState;
|
||||
use super::app_state::AppState;
|
||||
|
||||
pub struct PopupSurfaceParams<'a> {
|
||||
pub compositor: &'a WlCompositor,
|
||||
|
|
@ -29,7 +29,7 @@ pub struct PopupSurfaceParams<'a> {
|
|||
pub parent_layer_surface: &'a ZwlrLayerSurfaceV1,
|
||||
pub fractional_scale_manager: Option<&'a WpFractionalScaleManagerV1>,
|
||||
pub viewporter: Option<&'a WpViewporter>,
|
||||
pub queue_handle: &'a QueueHandle<WindowState>,
|
||||
pub queue_handle: &'a QueueHandle<AppState>,
|
||||
pub popup_config: PopupConfig,
|
||||
pub physical_size: PhysicalSize,
|
||||
pub scale_factor: f32,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::rendering::slint_integration::platform::CustomSlintPlatform;
|
|||
|
||||
use super::surface_state::WindowState;
|
||||
|
||||
struct PlatformWrapper(Rc<CustomSlintPlatform>);
|
||||
pub struct PlatformWrapper(pub Rc<CustomSlintPlatform>);
|
||||
|
||||
impl Platform for PlatformWrapper {
|
||||
fn create_window_adapter(&self) -> StdResult<Rc<dyn WindowAdapter>, PlatformError> {
|
||||
|
|
@ -107,7 +107,10 @@ impl WindowStateBuilder {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_compilation_result(mut self, compilation_result: Option<Rc<CompilationResult>>) -> Self {
|
||||
pub fn with_compilation_result(
|
||||
mut self,
|
||||
compilation_result: Option<Rc<CompilationResult>>,
|
||||
) -> Self {
|
||||
self.compilation_result = compilation_result;
|
||||
self
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ use layer_shika_adapters::platform::slint::ComponentHandle;
|
|||
use layer_shika_adapters::platform::slint_interpreter::{
|
||||
CompilationResult, ComponentDefinition, ComponentInstance,
|
||||
};
|
||||
use layer_shika_adapters::{PopupManager, WaylandWindowConfig, WindowState, WindowingSystemFacade};
|
||||
use layer_shika_adapters::{
|
||||
AppState, PopupManager, WaylandWindowConfig, WindowState, WindowingSystemFacade,
|
||||
};
|
||||
use layer_shika_domain::config::WindowConfig;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
|
|
@ -49,8 +51,8 @@ impl EventLoopHandle {
|
|||
let loop_handle = system.borrow().inner_ref().event_loop_handle();
|
||||
|
||||
loop_handle
|
||||
.insert_source(source, move |event, metadata, window_state| {
|
||||
let runtime_state = RuntimeState { window_state };
|
||||
.insert_source(source, move |event, metadata, app_state| {
|
||||
let runtime_state = RuntimeState { app_state };
|
||||
callback(event, metadata, runtime_state)
|
||||
})
|
||||
.map_err(|e| {
|
||||
|
|
@ -120,22 +122,42 @@ impl EventLoopHandle {
|
|||
}
|
||||
|
||||
pub struct RuntimeState<'a> {
|
||||
window_state: &'a mut WindowState,
|
||||
app_state: &'a mut AppState,
|
||||
}
|
||||
|
||||
impl RuntimeState<'_> {
|
||||
#[must_use]
|
||||
pub fn component_instance(&self) -> &ComponentInstance {
|
||||
self.window_state.component_instance()
|
||||
pub fn component_instance(&self) -> Option<&ComponentInstance> {
|
||||
self.app_state
|
||||
.primary_output()
|
||||
.map(WindowState::component_instance)
|
||||
}
|
||||
|
||||
pub fn all_component_instances(&self) -> impl Iterator<Item = &ComponentInstance> {
|
||||
self.app_state
|
||||
.all_outputs()
|
||||
.map(WindowState::component_instance)
|
||||
}
|
||||
|
||||
fn active_or_primary_output(&self) -> Option<&WindowState> {
|
||||
self.app_state
|
||||
.active_output()
|
||||
.and_then(|key| self.app_state.get_output_by_key(key))
|
||||
.or_else(|| self.app_state.primary_output())
|
||||
}
|
||||
|
||||
pub fn render_frame_if_dirty(&mut self) -> Result<()> {
|
||||
Ok(self.window_state.render_frame_if_dirty()?)
|
||||
for window in self.app_state.all_outputs() {
|
||||
window.render_frame_if_dirty()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
|
||||
self.window_state.compilation_result()
|
||||
self.app_state
|
||||
.primary_output()
|
||||
.and_then(WindowState::compilation_result)
|
||||
}
|
||||
|
||||
pub fn show_popup(
|
||||
|
|
@ -162,7 +184,19 @@ impl RuntimeState<'_> {
|
|||
|
||||
self.close_current_popup()?;
|
||||
|
||||
let popup_manager = self.window_state.popup_manager().ok_or_else(|| {
|
||||
let is_using_active = self.app_state.active_output().is_some();
|
||||
let active_window = self.active_or_primary_output().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "No active or primary output available".to_string(),
|
||||
})
|
||||
})?;
|
||||
|
||||
log::info!(
|
||||
"Creating popup on {} output",
|
||||
if is_using_active { "active" } else { "primary" }
|
||||
);
|
||||
|
||||
let popup_manager = active_window.popup_manager().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "No popup manager available".to_string(),
|
||||
})
|
||||
|
|
@ -209,16 +243,20 @@ impl RuntimeState<'_> {
|
|||
}
|
||||
|
||||
pub fn close_popup(&mut self, handle: PopupHandle) -> Result<()> {
|
||||
if let Some(popup_manager) = self.window_state.popup_manager() {
|
||||
if let Some(active_window) = self.active_or_primary_output() {
|
||||
if let Some(popup_manager) = active_window.popup_manager() {
|
||||
popup_manager.close(handle)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close_current_popup(&mut self) -> Result<()> {
|
||||
if let Some(popup_manager) = self.window_state.popup_manager() {
|
||||
if let Some(active_window) = self.active_or_primary_output() {
|
||||
if let Some(popup_manager) = active_window.popup_manager() {
|
||||
popup_manager.close_current_popup();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +267,13 @@ impl RuntimeState<'_> {
|
|||
height: f32,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
) -> Result<()> {
|
||||
let popup_manager = self.window_state.popup_manager().ok_or_else(|| {
|
||||
let active_window = self.active_or_primary_output().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "No active or primary output available".to_string(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let popup_manager = active_window.popup_manager().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "No popup manager available".to_string(),
|
||||
})
|
||||
|
|
@ -322,7 +366,7 @@ impl WindowingSystem {
|
|||
compilation_result,
|
||||
config,
|
||||
);
|
||||
let inner = layer_shika_adapters::WaylandWindowingSystem::new(wayland_config)?;
|
||||
let inner = layer_shika_adapters::WaylandWindowingSystem::new(&wayland_config)?;
|
||||
let facade = WindowingSystemFacade::new(inner);
|
||||
let inner_rc = Rc::new(RefCell::new(facade));
|
||||
|
||||
|
|
@ -339,7 +383,7 @@ impl WindowingSystem {
|
|||
};
|
||||
|
||||
system.setup_popup_command_handler(receiver)?;
|
||||
system.register_popup_callbacks()?;
|
||||
system.register_popup_callbacks();
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
|
@ -349,9 +393,9 @@ impl WindowingSystem {
|
|||
let sender_for_handler = self.popup_command_sender.clone();
|
||||
|
||||
loop_handle
|
||||
.insert_source(receiver, move |event, (), window_state| {
|
||||
.insert_source(receiver, move |event, (), app_state| {
|
||||
if let channel::Event::Msg(command) = event {
|
||||
let mut runtime_state = RuntimeState { window_state };
|
||||
let mut runtime_state = RuntimeState { app_state };
|
||||
|
||||
match command {
|
||||
PopupCommand::Show(request) => {
|
||||
|
|
@ -395,11 +439,15 @@ impl WindowingSystem {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn register_popup_callbacks(&self) -> Result<()> {
|
||||
self.with_component_instance(|component_instance| {
|
||||
self.callback_contract
|
||||
fn register_popup_callbacks(&self) {
|
||||
self.with_all_component_instances(|component_instance| {
|
||||
if let Err(e) = self
|
||||
.callback_contract
|
||||
.register_on_main_component(component_instance)
|
||||
})
|
||||
{
|
||||
log::error!("Failed to register popup callbacks on output: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -434,10 +482,23 @@ impl WindowingSystem {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn with_component_instance<F, R>(&self, f: F) -> R
|
||||
pub fn with_component_instance<F, R>(&self, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&ComponentInstance) -> R,
|
||||
{
|
||||
f(self.inner.borrow().component_instance())
|
||||
let facade = self.inner.borrow();
|
||||
let instance = facade.component_instance()?;
|
||||
Ok(f(instance))
|
||||
}
|
||||
|
||||
pub fn with_all_component_instances<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(&ComponentInstance),
|
||||
{
|
||||
let facade = self.inner.borrow();
|
||||
let system = facade.inner_ref();
|
||||
for window in system.app_state().all_outputs() {
|
||||
f(window.component_instance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue