diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 84a21c3..77a3153 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -5,7 +5,7 @@ use raw_window_handle_05::HasRawDisplayHandle as HasRawDisplayHandle05; use crate::application::Application; use crate::monitor::{Monitor, MonitorId}; -use crate::window::{Window, WindowAttributes, WindowId}; +use crate::window::{PopupAttributes, Surface, WindowAttributes, WindowId}; use self::proxy::EventLoopProxy; @@ -33,13 +33,16 @@ pub trait EventLoopHandle: HasDisplayHandle { fn proxy(&self) -> Arc; /// Request to create a window. - fn create_window(&mut self, attributes: &WindowAttributes) -> Result<(), ()>; + fn create_toplevel(&mut self, attributes: &WindowAttributes) -> Result<(), ()>; + + /// Requests to create a popup. + fn create_popup(&mut self, parent: WindowId, attributes: &PopupAttributes) -> Result<(), ()>; fn num_windows(&self) -> usize; - fn get_window(&self, window_id: WindowId) -> Option<&dyn Window>; + fn get_window(&self, window_id: WindowId) -> Option<&dyn Surface>; - fn get_window_mut(&mut self, window_id: WindowId) -> Option<&mut dyn Window>; + fn get_window_mut(&mut self, window_id: WindowId) -> Option<&mut dyn Surface>; fn get_monitor(&self, monitor_id: MonitorId) -> Option<&dyn Monitor>; diff --git a/winit-core/src/window.rs b/winit-core/src/window.rs index 6b3fc62..dbe2966 100644 --- a/winit-core/src/window.rs +++ b/winit-core/src/window.rs @@ -1,18 +1,46 @@ // TODO figure out how to do WindowId. +use std::any::Any; + pub use raw_window_handle::HasWindowHandle; pub use raw_window_handle_05::HasRawWindowHandle as HasRawWindowHandle05; -use crate::dpi::{LogicalSize, PhysicalSize, Position, Size}; +use crate::dpi::{ + self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, +}; use crate::monitor::MonitorId; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct WindowId(pub u128); -/// Common requests to perform on the window. -pub trait Window: HasWindowHandle + HasRawWindowHandle05 { +pub enum WindowRole<'a> { + Toplevel(&'a dyn Toplevel), + Popup(&'a dyn Popup), +} + +pub enum WindowRoleMut<'a> { + Toplevel(&'a mut dyn Toplevel), + Popup(&'a mut dyn Popup), +} + +/// Common API for all rendering targets. +pub trait Surface: HasWindowHandle + HasRawWindowHandle05 { fn id(&self) -> WindowId; + fn scale_factor(&self) -> f64; + + fn request_redraw(&mut self); + + fn inner_size(&self) -> PhysicalSize; + + /// Downcasts this surface to its specific type. + fn role(&self) -> WindowRole; + /// Downcasts this surface to its specific type. Returns a mutable reference. + fn role_mut(&mut self) -> WindowRoleMut; +} + +/// API for toplevel windows and dialogs. +pub trait Toplevel: Surface { /// Gets the current title of the window. fn title(&self) -> &str; @@ -22,12 +50,6 @@ pub trait Window: HasWindowHandle + HasRawWindowHandle05 { fn set_theme(&mut self, theme: Option); - fn scale_factor(&self) -> f64; - - fn request_redraw(&mut self); - - fn inner_size(&self) -> PhysicalSize; - fn set_minimized(&mut self, minimize: bool); fn set_maximized(&mut self, maximized: bool); @@ -37,6 +59,11 @@ pub trait Window: HasWindowHandle + HasRawWindowHandle05 { fn primary_monitor(&self) -> Option; } +/// API for popups. +pub trait Popup: Surface { + // fn set_position(&mut self, position: PhysicalPosition); +} + /// Attributes to use when creating a window. #[derive(Debug, Clone)] pub struct WindowAttributes { @@ -291,6 +318,119 @@ impl WindowAttributes { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum AnchorDirection { + Center = 0, + North = 1, + Northeast = 2, + East = 3, + Southeast = 4, + South = 5, + Southwest = 6, + West = 7, + Northwest = 8, +} + +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct AnchorHints: u32 { + const NONE = 0; + const FLIP_X = 1 << 0; + const FLIP_Y = 1 << 1; + const SLIDE_X = 1 << 2; + const SLIDE_Y = 1 << 3; + const RESIZE_X = 1 << 4; + const RESIZE_Y = 1 << 5; + } +} + +#[derive(Debug, Clone)] +pub struct PopupAttributes { + pub inner_size: Size, + pub anchor_rect: (Position, Size), + pub rect_anchor: AnchorDirection, + pub surface_anchor: AnchorDirection, + pub offset: Position, + pub anchor_hints: AnchorHints, +} + +impl Default for PopupAttributes { + fn default() -> Self { + Self { + inner_size: LogicalSize::new(100.0, 75.0).into(), + anchor_rect: ( + LogicalPosition::new(0.0, 0.0).into(), + LogicalSize::new(100.0, 100.0).into(), + ), + rect_anchor: AnchorDirection::Center, + surface_anchor: AnchorDirection::Center, + offset: LogicalPosition::new(0.0, 0.0).into(), + anchor_hints: AnchorHints::NONE, + } + } +} + +impl PopupAttributes { + pub fn inner_size(&self) -> Size { + self.inner_size + } + + pub fn with_inner_size>(mut self, size: S) -> Self { + self.inner_size = size.into(); + self + } + + pub fn anchor_rect(&self) -> (Position, Size) { + self.anchor_rect + } + + pub fn with_anchor_rect, S: Into>( + mut self, + top_left: P, + size: S, + ) -> Self { + self.anchor_rect = (top_left.into(), size.into()); + self + } + + pub fn rect_anchor(&self) -> AnchorDirection { + self.rect_anchor + } + + pub fn with_rect_anchor(mut self, direction: AnchorDirection) -> Self { + self.rect_anchor = direction; + self + } + + pub fn surface_anchor(&self) -> AnchorDirection { + self.surface_anchor + } + + pub fn with_surface_anchor(mut self, direction: AnchorDirection) -> Self { + self.surface_anchor = direction; + self + } + + pub fn offset(&self) -> Position { + self.offset + } + + pub fn with_offset>(mut self, offset: P) -> Self { + self.offset = offset.into(); + self + } + + pub fn anchor_hints(&self) -> AnchorHints { + self.anchor_hints + } + + pub fn with_anchor_hints(mut self, hints: AnchorHints) -> Self { + self.anchor_hints = hints; + self + } +} + /// A window level groups windows with respect to their z-position. /// /// The relative ordering between windows in different window levels is fixed. diff --git a/winit-wayland/examples/window.rs b/winit-wayland/examples/window.rs index 2bd4a96..fc2ae98 100644 --- a/winit-wayland/examples/window.rs +++ b/winit-wayland/examples/window.rs @@ -4,13 +4,14 @@ use std::time::Duration; use winit_core::application::{Application, ApplicationWindow, StartCause}; use winit_core::dpi::PhysicalSize; use winit_core::event_loop::{EventLoopHandle, EventLoopRequests}; -use winit_core::window::WindowId; +use winit_core::window::{Toplevel, WindowId, WindowRole}; use winit_wayland::event_loop::EventLoop; use winit_wayland::MyCoolTrait; use softbuffer::{Context, Surface}; const DARK_GRAY: u32 = 0xFF181818; +const ORANGE: u32 = 0xFF_FF8000; pub struct State { context: Context, @@ -39,7 +40,7 @@ impl Application for State { fn new_events(&mut self, loop_handle: &mut dyn EventLoopHandle, start_cause: StartCause) { println!("Start cause {start_cause:?}"); - let _ = loop_handle.create_window(&Default::default()); + let _ = loop_handle.create_toplevel(&Default::default()); } fn about_to_wait(&mut self, _: &mut dyn EventLoopHandle) { @@ -89,17 +90,33 @@ impl ApplicationWindow for State { _ => return, }; - if let Some(monitor_id) = window.current_monitor() { - let monitor = loop_handle.get_monitor(monitor_id).unwrap(); - println!("Current monitor name {:?}", monitor.name()); + match window.role() { + WindowRole::Toplevel(toplevel) => { + if let Some(monitor_id) = toplevel.current_monitor() { + let monitor = loop_handle.get_monitor(monitor_id).unwrap(); + println!("Current monitor name {:?}", monitor.name()); + } + + let size = window.inner_size(); + let _ = surface.resize( + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + let mut buffer = surface.buffer_mut().unwrap(); + buffer.fill(DARK_GRAY); + buffer.present().unwrap(); + }, + WindowRole::Popup(popup) => { + let size = window.inner_size(); + let _ = surface.resize( + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + let mut buffer = surface.buffer_mut().unwrap(); + buffer.fill(ORANGE); + buffer.present().unwrap(); + }, } - - let size = window.inner_size(); - let _ = surface - .resize(NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap()); - let mut buffer = surface.buffer_mut().unwrap(); - buffer.fill(DARK_GRAY); - buffer.present().unwrap(); } fn destroyed(&mut self, loop_handle: &mut dyn EventLoopHandle, _: WindowId) { diff --git a/winit-wayland/src/event_loop.rs b/winit-wayland/src/event_loop.rs index fc8a560..8b664c0 100644 --- a/winit-wayland/src/event_loop.rs +++ b/winit-wayland/src/event_loop.rs @@ -20,7 +20,7 @@ use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState}; use winit_core::application::Application; use winit_core::event_loop::proxy::EventLoopProxy as CoreEventLoopProxy; use winit_core::event_loop::{EventLoopHandle, EventLoopRequests}; -use winit_core::window::{Window as CoreWindow, WindowId}; +use winit_core::window::{Surface as CoreSurface, WindowId}; use crate::state::WinitState; use crate::MyCoolTrait; @@ -93,7 +93,7 @@ impl EventLoopRequests for EventLoop { let user = self.state.user.as_mut().unwrap(); for (window_id, window) in &mut winit.windows { - if mem::take(&mut window.redraw) { + if window.take_redraw() { redraw.push(*window_id); } } diff --git a/winit-wayland/src/lib.rs b/winit-wayland/src/lib.rs index 9e6687f..4c7a945 100644 --- a/winit-wayland/src/lib.rs +++ b/winit-wayland/src/lib.rs @@ -13,6 +13,8 @@ use winit_core::window::WindowId; pub mod event_loop; pub mod monitor; pub mod state; +pub mod toplevel; +pub mod popup; pub mod window; /// Get the WindowId out of the surface. diff --git a/winit-wayland/src/popup.rs b/winit-wayland/src/popup.rs new file mode 100644 index 0000000..03e18cd --- /dev/null +++ b/winit-wayland/src/popup.rs @@ -0,0 +1,296 @@ +use std::{marker::PhantomData, ops::Deref, ptr::NonNull, sync::Arc}; + +use raw_window_handle::{ + HandleError, HasWindowHandle, RawWindowHandle, WaylandWindowHandle, WindowHandle, +}; +use raw_window_handle_05::HasRawWindowHandle as HasRawWindowHandle05; +use sctk::{ + compositor::CompositorState, + shell::{ + self, + xdg::{ + popup::{Popup as XdgPopup, PopupConfigure, PopupHandler}, + XdgPositioner, XdgSurface, + }, + }, +}; +use wayland_client::{Dispatch, Proxy}; +use wayland_protocols::{ + wp::{ + fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1, + viewporter::client::wp_viewport::WpViewport, + }, + xdg::shell::client::xdg_positioner, +}; +use winit_core::{ + application::Application, + dpi::{LogicalSize, PhysicalSize, Size}, + window::{ + AnchorDirection, AnchorHints, Popup as WinitPopup, PopupAttributes, + Surface as WinitSurface, Theme, WindowAttributes, WindowId, WindowRole, WindowRoleMut, + }, +}; + +use crate::{event_loop::RuntimeState, state::WinitState, window::Window}; + +pub struct Popup { + /// The last received configure. + pub last_configure: Option, + + pub viewport: Option, + fractional_scale: Option, + + /// The scale factor of the window. + pub scale_factor: f64, + + /// The inner size of the window, as in without client side decorations. + size: LogicalSize, + + /// Initial window size provided by the user. Removed on the first + /// configure. + initial_size: Option, + + compositor: Arc, + + /// Whether the window is transparent. + transparent: bool, + + pub redraw: bool, + + // Note, should be the last since it drops the surface. + pub popup: XdgPopup, + + _phantom_tparam_do_not_use: PhantomData, +} + +impl Popup { + pub fn new( + winit: &mut WinitState, + parent: WindowId, + attributes: &PopupAttributes, + ) -> Result { + let parent = winit.windows.get(&parent).ok_or(())?; + + let compositor = winit.compositor.clone(); + let surface = compositor.create_surface(&winit.queue_handle); + + let positioner = { + let positioner = XdgPositioner::new(&winit.xdg_shell).unwrap(); + + let anchor_top_left = attributes.anchor_rect().0.to_physical::(1.0); + let anchor_size = attributes.anchor_rect().1.to_physical::(1.0); + positioner.set_anchor_rect( + anchor_top_left.x, + anchor_top_left.y, + anchor_size.width, + anchor_size.height, + ); + + let inner_size = attributes.inner_size().to_physical::(1.0); + positioner.set_size(inner_size.width, inner_size.height); + + positioner.set_gravity(to_xdg_positioner_gravity(attributes.surface_anchor())); + positioner.set_anchor(to_xdg_positioner_anchor(attributes.rect_anchor())); + + positioner + .set_constraint_adjustment(to_xdg_constraints(attributes.anchor_hints()).bits()); + + positioner + }; + + let popup = XdgPopup::new( + parent.xdg_surface(), + &positioner.deref(), + &winit.queue_handle, + &*winit.compositor, + &winit.xdg_shell, + ) + .unwrap(); + let size = attributes.inner_size(); + + Ok(Self { + last_configure: None, + viewport: None, + fractional_scale: None, + scale_factor: 1.0, + size: size.to_logical(1.0), + initial_size: Some(size), + compositor, + transparent: true, + redraw: false, + popup, + _phantom_tparam_do_not_use: PhantomData, + }) + } + + pub fn configured(&self) -> bool { + self.last_configure.is_some() + } +} + +impl WinitSurface for Popup { + fn id(&self) -> WindowId { + crate::make_wid(self.popup.wl_surface()) + } + + fn scale_factor(&self) -> f64 { + self.scale_factor + } + + fn request_redraw(&mut self) { + self.redraw = true; + } + + fn inner_size(&self) -> winit_core::dpi::PhysicalSize { + crate::logical_to_physical_rounded(self.size, self.scale_factor) + } + + fn role(&self) -> WindowRole<'_> { + WindowRole::Popup(self) + } + + fn role_mut(&mut self) -> WindowRoleMut { + WindowRoleMut::Popup(self) + } +} + +impl WinitPopup for Popup {} + +impl HasWindowHandle for Popup { + fn window_handle( + &self, + ) -> Result, raw_window_handle::HandleError> { + let ptr = self.popup.wl_surface().id().as_ptr(); + let handle = WaylandWindowHandle::new({ + NonNull::new(ptr as *mut _).expect("wl_surface should never be null") + }); + + unsafe { Ok(WindowHandle::borrow_raw(handle.into())) } + } +} + +unsafe impl HasRawWindowHandle05 for Popup { + fn raw_window_handle(&self) -> raw_window_handle_05::RawWindowHandle { + let ptr = self.popup.wl_surface().id().as_ptr(); + let mut window_handle = raw_window_handle_05::WaylandWindowHandle::empty(); + window_handle.surface = ptr as *mut _; + + raw_window_handle_05::RawWindowHandle::Wayland(window_handle) + } +} + +impl PopupHandler for RuntimeState { + fn configure( + &mut self, + _: &wayland_client::Connection, + queue_handle: &wayland_client::QueueHandle, + popup: &XdgPopup, + configure: PopupConfigure, + ) { + let winit = &mut self.winit; + let window_id = crate::make_wid(popup.wl_surface()); + let popup = match winit.windows.get_mut(&window_id) { + Some(Window::Popup(window)) => window, + _ => return, + }; + + let scale_factor = popup.scale_factor; + + if let Some(initial_size) = popup.initial_size.take() { + popup.size = initial_size.to_logical(scale_factor); + } + + let user = self.user.as_mut().unwrap(); + let new_size = LogicalSize::::new(configure.width as u32, configure.height as u32); + let initial_configure = popup.last_configure.is_none(); + + if initial_configure { + user.created(winit, window_id); + user.scale_factor_changed(winit, window_id, scale_factor); + } + + user.resized(winit, window_id, crate::logical_to_physical_rounded(new_size, scale_factor)); + + if initial_configure { + user.redraw_requested(winit, window_id); + } + } + + fn done( + &mut self, + _: &wayland_client::Connection, + qh: &wayland_client::QueueHandle, + popup: &XdgPopup, + ) { + let winit = &mut self.winit; + let window_id = crate::make_wid(popup.wl_surface()); + let popup = match winit.windows.get_mut(&window_id) { + Some(Window::Popup(window)) => window, + _ => return, + }; + + // todo: figure out what the hell to do here + } +} + +impl Dispatch for RuntimeState { + fn event( + state: &mut Self, + proxy: &xdg_positioner::XdgPositioner, + event: ::Event, + data: &U, + conn: &wayland_client::Connection, + qhandle: &wayland_client::QueueHandle, + ) { + // nothing, positioners do not generate events + } +} + +fn to_xdg_positioner_anchor(dir: AnchorDirection) -> xdg_positioner::Anchor { + match dir { + AnchorDirection::Center => xdg_positioner::Anchor::None, + AnchorDirection::North => xdg_positioner::Anchor::Top, + AnchorDirection::Northeast => xdg_positioner::Anchor::TopRight, + AnchorDirection::East => xdg_positioner::Anchor::Right, + AnchorDirection::Southeast => xdg_positioner::Anchor::BottomRight, + AnchorDirection::South => xdg_positioner::Anchor::Bottom, + AnchorDirection::Southwest => xdg_positioner::Anchor::BottomLeft, + AnchorDirection::West => xdg_positioner::Anchor::Left, + AnchorDirection::Northwest => xdg_positioner::Anchor::TopLeft, + } +} +fn to_xdg_positioner_gravity(dir: AnchorDirection) -> xdg_positioner::Gravity { + match dir { + AnchorDirection::Center => xdg_positioner::Gravity::None, + AnchorDirection::North => xdg_positioner::Gravity::Top, + AnchorDirection::Northeast => xdg_positioner::Gravity::TopRight, + AnchorDirection::East => xdg_positioner::Gravity::Right, + AnchorDirection::Southeast => xdg_positioner::Gravity::BottomRight, + AnchorDirection::South => xdg_positioner::Gravity::Bottom, + AnchorDirection::Southwest => xdg_positioner::Gravity::BottomLeft, + AnchorDirection::West => xdg_positioner::Gravity::Left, + AnchorDirection::Northwest => xdg_positioner::Gravity::TopLeft, + } +} +fn to_xdg_constraints(flags: AnchorHints) -> xdg_positioner::ConstraintAdjustment { + let mut res = xdg_positioner::ConstraintAdjustment::None; + if flags.contains(AnchorHints::SLIDE_X) { + res |= xdg_positioner::ConstraintAdjustment::SlideX; + } + if flags.contains(AnchorHints::SLIDE_Y) { + res |= xdg_positioner::ConstraintAdjustment::SlideY; + } + if flags.contains(AnchorHints::FLIP_X) { + res |= xdg_positioner::ConstraintAdjustment::FlipX; + } + if flags.contains(AnchorHints::FLIP_Y) { + res |= xdg_positioner::ConstraintAdjustment::FlipY; + } + if flags.contains(AnchorHints::RESIZE_X) { + res |= xdg_positioner::ConstraintAdjustment::ResizeX; + } + if flags.contains(AnchorHints::RESIZE_Y) { + res |= xdg_positioner::ConstraintAdjustment::ResizeY; + } + res +} diff --git a/winit-wayland/src/state.rs b/winit-wayland/src/state.rs index 7c6fb8e..bb0a369 100644 --- a/winit-wayland/src/state.rs +++ b/winit-wayland/src/state.rs @@ -28,23 +28,33 @@ use sctk::subcompositor::SubcompositorState; use winit_core::application::Application; use winit_core::event_loop::proxy::EventLoopProxy as CoreEventLoopProxy; use winit_core::event_loop::EventLoopHandle; -use winit_core::monitor::{Monitor as CoreMonitor, MonitorId}; -use winit_core::window::{Window as CoreWindow, WindowAttributes, WindowId}; +use winit_core::monitor::{Monitor as WinitMonitor, MonitorId}; +use winit_core::window::{Surface as WinitSurface, WindowAttributes, WindowId}; use crate::monitor::Monitor; -use crate::window::Window; +use crate::popup::Popup; +use crate::toplevel::Toplevel; use crate::event_loop::{EventLoopProxy, RuntimeState}; +use crate::window::Window; impl EventLoopHandle for WinitState { fn proxy(&self) -> Arc { self.proxy.clone() } - fn create_window(&mut self, attributes: &WindowAttributes) -> Result<(), ()> { - let window = Window::new(self, attributes); + fn create_toplevel(&mut self, attributes: &WindowAttributes) -> Result<(), ()> { + let window = Toplevel::new(self, attributes); let window_id = window.id(); - self.windows.insert(window_id, window); + self.windows.insert(window_id, Window::Toplevel(window)); + Ok(()) + } + + // TODO(jgcodes2020): can/should we ensure window type safety here? + fn create_popup(&mut self, parent: WindowId, attributes: &winit_core::window::PopupAttributes) -> Result<(), ()> { + let popup = Popup::new(self, parent, attributes)?; + let popup_id = popup.id(); + self.windows.insert(popup_id, Window::Popup(popup)); Ok(()) } @@ -52,22 +62,22 @@ impl EventLoopHandle for WinitState { self.windows.len() } - fn get_window(&self, window_id: WindowId) -> Option<&dyn CoreWindow> { + fn get_window(&self, window_id: WindowId) -> Option<&dyn WinitSurface> { let window = self.windows.get(&window_id)?; - if window.last_configure.is_none() { - return None; + if window.configured() { + Some(window as &dyn WinitSurface) } else { - Some(window as &dyn CoreWindow) + None } } - fn get_window_mut(&mut self, window_id: WindowId) -> Option<&mut dyn CoreWindow> { + fn get_window_mut(&mut self, window_id: WindowId) -> Option<&mut dyn WinitSurface> { let window = self.windows.get_mut(&window_id)?; - if window.last_configure.is_none() { - return None; + if window.configured() { + Some(window as &mut dyn WinitSurface) } else { - Some(window as &mut dyn CoreWindow) + None } } @@ -75,16 +85,18 @@ impl EventLoopHandle for WinitState { self.exit = true; } - fn get_monitor(&self, monitor_id: MonitorId) -> Option<&dyn CoreMonitor> { + fn get_monitor(&self, monitor_id: MonitorId) -> Option<&dyn WinitMonitor> { self.monitors .iter() .find(|monitor| monitor.id() == monitor_id) - .map(|monitor| monitor as &dyn CoreMonitor) + .map(|monitor| monitor as &dyn WinitMonitor) } - fn monitors(&self) -> Vec<&dyn CoreMonitor> { - self.monitors.iter().map(|monitor| monitor as &dyn CoreMonitor).collect() + fn monitors(&self) -> Vec<&dyn WinitMonitor> { + self.monitors.iter().map(|monitor| monitor as &dyn WinitMonitor).collect() } + + } impl HasDisplayHandle for WinitState { @@ -199,9 +211,13 @@ impl WinitState { ) { let winit = &mut state.winit; let window_id = crate::make_wid(surface); + // let window = match winit.windows.get_mut(&window_id) { + // Some(window) => window, + // None => return, + // }; let window = match winit.windows.get_mut(&window_id) { - Some(window) => window, - None => return, + Some(Window::Toplevel(window)) => window, + _ => return, }; window.set_scale_factor(scale_factor); @@ -302,3 +318,4 @@ sctk::delegate_shm!(@ RuntimeState); sctk::delegate_compositor!(@ RuntimeState); sctk::delegate_xdg_shell!(@ RuntimeState); sctk::delegate_xdg_window!(@ RuntimeState); +sctk::delegate_xdg_popup!(@ RuntimeState); diff --git a/winit-wayland/src/toplevel.rs b/winit-wayland/src/toplevel.rs new file mode 100644 index 0000000..0c3ed6f --- /dev/null +++ b/winit-wayland/src/toplevel.rs @@ -0,0 +1,516 @@ +use std::num::NonZeroU32; +use std::sync::Arc; + +use raw_window_handle::{HandleError, HasWindowHandle, WaylandWindowHandle, WindowHandle}; +use raw_window_handle_05::HasRawWindowHandle as HasRawWindowHandle05; + +use sctk::compositor::{CompositorHandler, CompositorState, Region, SurfaceData}; +use sctk::reexports::client::Proxy; +use sctk::reexports::csd_frame::{ + DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState, +}; +use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; +use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3; +use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport; +use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge; +use sctk::shell::xdg::window::{ + DecorationMode, Window as XdgWindow, WindowConfigure, WindowDecorations, WindowHandler, +}; +use sctk::shell::xdg::XdgSurface; +use sctk::shell::WaylandSurface; + +use wayland_client::{Connection, QueueHandle}; +use winit_core::application::Application; +use winit_core::dpi::{LogicalSize, PhysicalSize, Size}; +use winit_core::monitor::MonitorId; +use winit_core::window::{ + Popup as WinitPopup, Surface as WinitSurface, Theme, Toplevel as WinitToplevel, + WindowAttributes, WindowId, WindowRole, WindowRoleMut, +}; + +use crate::event_loop::RuntimeState; +use crate::logical_to_physical_rounded; +use crate::monitor::Monitor; +use crate::state::WinitState; +use crate::window::Window; + +// Minimum window inner size. +const MIN_WINDOW_SIZE: LogicalSize = LogicalSize::new(2, 1); + +#[cfg(feature = "sctk-adwaita")] +type WinitFrame = sctk_adwaita::AdwaitaFrame>; +#[cfg(not(feature = "sctk-adwaita"))] +type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame; + +pub struct Toplevel { + /// The last received configure. + pub last_configure: Option, + + pub viewport: Option, + fractional_scale: Option, + + /// The window frame, which is created from the configure request. + frame: Option>, + + /// The scale factor of the window. + pub scale_factor: f64, + + /// The latest requested window title. + title: String, + + /// Whether the window has focus. + has_focus: bool, + + /// Whether the frame is resizable. + resizable: bool, + + /// The inner size of the window, as in without client side decorations. + size: LogicalSize, + + /// The size of the window when no states were applied to it. The primary + /// use for it is to fallback to original window size, before it was + /// maximized, if the compositor sends `None` for the new size in the + /// configure. + stateless_size: LogicalSize, + + /// Initial window size provided by the user. Removed on the first + /// configure. + initial_size: Option, + + compositor: Arc, + + /// Theme varaint. + theme: Option, + + /// Min size. + min_inner_size: LogicalSize, + max_inner_size: Option>, + + /// Whether we should decorate the frame. + decorate: bool, + + /// Whether the window is transparent. + transparent: bool, + + /// Whether the CSD fail to create, so we don't try to create them on each + /// iteration. + csd_fails: bool, + + pub redraw: bool, + + // Note, should be the last since it drops the surface. + pub window: XdgWindow, +} + +impl Toplevel { + pub fn new(winit: &mut WinitState, attributes: &WindowAttributes) -> Self { + let compositor = winit.compositor.clone(); + let surface = compositor.create_surface(&winit.queue_handle); + + // We prefer server side decorations, however to not have decorations we ask for + // client side decorations instead. + let decorate = if attributes.decorations() { + WindowDecorations::RequestServer + } else { + WindowDecorations::RequestClient + }; + + let window = winit.xdg_shell.create_window(surface.clone(), decorate, &winit.queue_handle); + + let viewport = None; + let fractional_scale = None; + + let size = attributes.inner_size(); + + // Create the window with some defaults. + let mut window = Self { + decorate: attributes.decorations(), + min_inner_size: MIN_WINDOW_SIZE, + stateless_size: size.to_logical(1.), + size: size.to_logical(1.), + initial_size: Some(size), + max_inner_size: None, + last_configure: None, + transparent: true, + fractional_scale, + scale_factor: 1., + has_focus: false, + csd_fails: false, + resizable: true, + redraw: false, + frame: None, + theme: None, + compositor, + viewport, + window, + title: Default::default(), + }; + + window.set_title(attributes.title()); + + // Set transparency hint. + window.set_transparent(attributes.transparent); + + window.set_min_inner_size(attributes.min_inner_size()); + window.set_max_inner_size(attributes.max_inner_size()); + + window.set_resizable(attributes.resizable()); + + // window.set_ + + if attributes.maximized() { + window.set_maximized(true); + } + + // TODO: platform attributes. + + // NOTE: initial commit for the window. + window.window.commit(); + + window + } + + /// Set the resizable state on the window. + #[inline] + pub fn set_resizable(&mut self, resizable: bool) { + if self.resizable == resizable { + return; + } + + self.resizable = resizable; + + if resizable { + // Restore min/max sizes of the window. + self.reload_min_max_hints(); + } else { + self.set_min_inner_size(Some(self.size.into())); + self.set_max_inner_size(Some(self.size.into())); + } + + // Reload the state on the frame as well. + if let Some(frame) = self.frame.as_mut() { + frame.set_resizable(resizable); + } + } + + pub fn set_transparent(&mut self, transparent: bool) { + self.transparent = transparent; + self.reload_transparency_hint(); + } + + pub fn set_min_inner_size(&mut self, size: Option) { + let mut size = + size.map(|size| size.to_logical(self.scale_factor)).unwrap_or(MIN_WINDOW_SIZE); + size.height = size.width.max(MIN_WINDOW_SIZE.width); + size.width = size.width.max(MIN_WINDOW_SIZE.width); + // TODO borders + self.window.set_min_size(Some(size.into())); + self.min_inner_size = size; + } + + pub fn set_max_inner_size(&mut self, size: Option) { + let size = size.map(|size| size.to_logical(self.scale_factor)); + self.window.set_max_size(size.map(Into::into)); + self.max_inner_size = size; + } + + pub(crate) fn resize(&mut self, new_size: LogicalSize) { + self.size = new_size; + + // Update the stateless size. + if Some(true) == self.last_configure.as_ref().map(Self::is_stateless) { + self.stateless_size = self.size; + } + + // Update the inner frame. + let ((x, y), outer_size) = if let Some(frame) = self.frame.as_mut() { + // Resize only visible frame. + if !frame.is_hidden() { + frame.resize( + NonZeroU32::new(self.size.width).unwrap(), + NonZeroU32::new(self.size.height).unwrap(), + ); + } + + (frame.location(), frame.add_borders(self.size.width, self.size.height).into()) + } else { + ((0, 0), self.size) + }; + + // Reload the hint. + self.reload_transparency_hint(); + + // Set the window geometry. + self.window.xdg_surface().set_window_geometry( + x, + y, + outer_size.width as i32, + outer_size.height as i32, + ); + + // Update the target viewport, this is used if and only if fractional scaling is + // in use. + if let Some(viewport) = self.viewport.as_ref() { + // Set inner size without the borders. + viewport.set_destination(self.size.width as _, self.size.height as _); + } + } + + #[inline] + pub(crate) fn is_stateless(configure: &WindowConfigure) -> bool { + !(configure.is_maximized() || configure.is_fullscreen() || configure.is_tiled()) + } + + pub(crate) fn set_scale_factor(&mut self, scale_factor: f64) { + self.scale_factor = scale_factor; + + // NOTE: When fractional scaling is not used update the buffer scale. + if self.fractional_scale.is_none() { + let _ = self.window.set_buffer_scale(self.scale_factor as _); + } + + if let Some(frame) = self.frame.as_mut() { + frame.set_scaling_factor(scale_factor); + } + } + + /// Reissue the transparency hint to the compositor. + pub(crate) fn reload_transparency_hint(&self) { + let surface = self.window.wl_surface(); + + if self.transparent { + surface.set_opaque_region(None); + } else if let Ok(region) = Region::new(&*self.compositor) { + region.add(0, 0, i32::MAX, i32::MAX); + surface.set_opaque_region(Some(region.wl_region())); + } else { + // warn!("Failed to mark window opaque."); + } + } + + /// Reload the hints for minimum and maximum sizes. + pub(crate) fn reload_min_max_hints(&mut self) { + self.set_min_inner_size(Some(self.min_inner_size.into())); + self.set_max_inner_size(self.max_inner_size.map(Into::into)); + } + + pub(crate) fn configured(&self) -> bool { + self.last_configure.is_some() + } +} + +impl HasWindowHandle for Toplevel { + fn window_handle(&self) -> Result, HandleError> { + let ptr = self.window.wl_surface().id().as_ptr(); + let handle = WaylandWindowHandle::new({ + std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface should never be null") + }); + + unsafe { Ok(WindowHandle::borrow_raw(handle.into())) } + } +} + +impl WinitSurface for Toplevel { + fn id(&self) -> WindowId { + crate::make_wid(&self.window.wl_surface()) + } + + fn request_redraw(&mut self) { + self.redraw = true; + } + + fn scale_factor(&self) -> f64 { + self.scale_factor + } + + fn inner_size(&self) -> PhysicalSize { + crate::logical_to_physical_rounded(self.size, self.scale_factor) + } + + fn role(&self) -> WindowRole<'_> { + WindowRole::Toplevel(self) + } + + fn role_mut(&mut self) -> WindowRoleMut<'_> { + WindowRoleMut::Toplevel(self) + } +} + +impl WinitToplevel for Toplevel { + fn title(&self) -> &str { + &self.title + } + + fn theme(&self) -> Option { + self.theme + } + + fn set_theme(&mut self, theme: Option) { + self.theme = theme; + #[cfg(feature = "sctk-adwaita")] + if let Some(frame) = self.frame.as_mut() { + frame.set_config(into_sctk_adwaita_config(theme)) + } + } + + fn set_title(&mut self, title: &str) { + let mut title = title.to_owned(); + // Truncate the title to at most 1024 bytes, so that it does not blow up the + // protocol messages + if title.len() > 1024 { + let mut new_len = 1024; + while !title.is_char_boundary(new_len) { + new_len -= 1; + } + title.truncate(new_len); + } + + // Update the CSD title. + if let Some(frame) = self.frame.as_mut() { + frame.set_title(&title); + } + + self.window.set_title(&title); + self.title = title; + } + + fn set_minimized(&mut self, minimize: bool) { + if minimize { + self.window.set_minimized(); + } + } + + fn set_maximized(&mut self, maximized: bool) { + if maximized { + self.window.set_maximized(); + } else { + self.window.unset_maximized(); + } + } + + fn current_monitor(&self) -> Option { + let data = self.window.wl_surface().data::()?; + data.outputs().next().as_ref().map(crate::make_mid) + } + + fn primary_monitor(&self) -> Option { + None + } +} + +impl WindowHandler for RuntimeState { + fn request_close(&mut self, _: &Connection, _: &QueueHandle, window: &XdgWindow) { + let window_id = crate::make_wid(window.wl_surface()); + let user_state = self.user.as_mut().unwrap(); + if user_state.close_requested(&mut self.winit, window_id) { + // Instantly drop the window. + drop(self.winit.windows.remove(&window_id)); + user_state.destroyed(&mut self.winit, window_id); + } + } + + fn configure( + &mut self, + _: &Connection, + queue_handle: &QueueHandle, + window: &XdgWindow, + configure: WindowConfigure, + _serial: u32, + ) { + let winit = &mut self.winit; + let window_id = crate::make_wid(window.wl_surface()); + let window = match winit.windows.get_mut(&window_id) { + Some(Window::Toplevel(window)) => window, + _ => return, + }; + + let scale_factor = window.scale_factor; + + // NOTE: when using fractional scaling or wl_compositor@v6 the scaling + // should be delivered before the first configure, thus apply it to + // properly scale the physical sizes provided by the users. + if let Some(initial_size) = window.initial_size.take() { + window.size = initial_size.to_logical(scale_factor); + window.stateless_size = window.size; + } + + if let Some(subcompositor) = winit.subcompositor.as_ref().filter(|_| { + configure.decoration_mode == DecorationMode::Client + && window.frame.is_none() + && !window.csd_fails + }) { + match WinitFrame::new( + &window.window, + &winit.shm, + #[cfg(feature = "sctk-adwaita")] + winit.compositor.clone(), + subcompositor.clone(), + queue_handle.clone(), + #[cfg(feature = "sctk-adwaita")] + into_sctk_adwaita_config(window.theme), + ) { + Ok(mut frame) => { + frame.set_title(&window.title); + frame.set_scaling_factor(scale_factor); + // Hide the frame if we were asked to not decorate. + frame.set_hidden(!window.decorate); + window.frame = Some(frame); + }, + Err(err) => { + // warn!("Failed to create client side decorations frame: {err}"); + window.csd_fails = true; + }, + } + } else if configure.decoration_mode == DecorationMode::Server { + // Drop the frame for server side decorations to save resources. + window.frame = None; + } + + let (new_size, constrain): (LogicalSize, bool) = match configure.new_size { + (Some(width), Some(height)) => ((width.get(), height.get()).into(), false), + _ => (window.size, true), + }; + + let user = self.user.as_mut().unwrap(); + let initial_configure = window.last_configure.is_none(); + window.last_configure = Some(configure); + + window.resize(new_size); + + if let Some(foo) = self.vtable.foo { + println!("Calling optional method!"); + foo(user); + } + + // NOTE: we consider window as created when its initial configure arrives, until + // then it's considered as not created and attempt to get it will result in + // error. + if initial_configure { + user.created(winit, window_id); + user.scale_factor_changed(winit, window_id, scale_factor); + } + + user.resized(winit, window_id, logical_to_physical_rounded(new_size, scale_factor)); + + if initial_configure { + user.redraw_requested(winit, window_id); + } + } +} + +unsafe impl HasRawWindowHandle05 for Toplevel { + fn raw_window_handle(&self) -> raw_window_handle_05::RawWindowHandle { + let mut window_handle = raw_window_handle_05::WaylandWindowHandle::empty(); + window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _; + raw_window_handle_05::RawWindowHandle::Wayland(window_handle) + } +} + +#[cfg(feature = "sctk-adwaita")] +fn into_sctk_adwaita_config(theme: Option) -> sctk_adwaita::FrameConfig { + match theme { + Some(Theme::Light) => sctk_adwaita::FrameConfig::light(), + Some(Theme::Dark) => sctk_adwaita::FrameConfig::dark(), + None => sctk_adwaita::FrameConfig::auto(), + } +} diff --git a/winit-wayland/src/window.rs b/winit-wayland/src/window.rs index 1b70060..ea48e74 100644 --- a/winit-wayland/src/window.rs +++ b/winit-wayland/src/window.rs @@ -1,502 +1,125 @@ -use std::num::NonZeroU32; -use std::sync::Arc; +use std::mem; use raw_window_handle::{HandleError, HasWindowHandle, WaylandWindowHandle, WindowHandle}; use raw_window_handle_05::HasRawWindowHandle as HasRawWindowHandle05; -use sctk::compositor::{CompositorHandler, CompositorState, Region, SurfaceData}; -use sctk::reexports::client::Proxy; -use sctk::reexports::csd_frame::{ - DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState, -}; -use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; -use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3; -use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport; -use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge; -use sctk::shell::xdg::window::{ - DecorationMode, Window as XdgWindow, WindowConfigure, WindowDecorations, WindowHandler, -}; -use sctk::shell::xdg::XdgSurface; -use sctk::shell::WaylandSurface; +use sctk::shell::{xdg::{window::WindowConfigure, XdgSurface}, WaylandSurface}; +use wayland_client::protocol::wl_surface::WlSurface; +use winit_core::{application::Application, window::Surface}; -use wayland_client::{Connection, QueueHandle}; -use winit_core::application::Application; -use winit_core::dpi::{LogicalSize, PhysicalSize, Size}; -use winit_core::monitor::MonitorId; -use winit_core::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId}; +use crate::{popup::Popup, toplevel::Toplevel}; -use crate::event_loop::RuntimeState; -use crate::logical_to_physical_rounded; -use crate::monitor::Monitor; -use crate::state::WinitState; -// Minimum window inner size. -const MIN_WINDOW_SIZE: LogicalSize = LogicalSize::new(2, 1); -#[cfg(feature = "sctk-adwaita")] -type WinitFrame = sctk_adwaita::AdwaitaFrame>; -#[cfg(not(feature = "sctk-adwaita"))] -type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame; - -pub struct Window { - /// The last received configure. - pub last_configure: Option, - - pub viewport: Option, - fractional_scale: Option, - - /// The window frame, which is created from the configure request. - frame: Option>, - - /// The scale factor of the window. - pub scale_factor: f64, - - /// The latest requested window title. - title: String, - - /// Whether the window has focus. - has_focus: bool, - - /// Whether the frame is resizable. - resizable: bool, - - /// The inner size of the window, as in without client side decorations. - size: LogicalSize, - - /// The size of the window when no states were applied to it. The primary - /// use for it is to fallback to original window size, before it was - /// maximized, if the compositor sends `None` for the new size in the - /// configure. - stateless_size: LogicalSize, - - /// Initial window size provided by the user. Removed on the first - /// configure. - initial_size: Option, - - compositor: Arc, - - /// Theme varaint. - theme: Option, - - /// Min size. - min_inner_size: LogicalSize, - max_inner_size: Option>, - - /// Whether we should decorate the frame. - decorate: bool, - - /// Whether the window is transparent. - transparent: bool, - - /// Whether the CSD fail to create, so we don't try to create them on each - /// iteration. - csd_fails: bool, - - pub redraw: bool, - - // Note, should be the last since it drops the surface. - pub window: XdgWindow, +pub(crate) enum Window { + Toplevel(Toplevel), + Popup(Popup) } impl Window { - pub fn new(winit: &mut WinitState, attributes: &WindowAttributes) -> Self { - let compositor = winit.compositor.clone(); - let surface = compositor.create_surface(&winit.queue_handle); - - // We prefer server side decorations, however to not have decorations we ask for - // client side decorations instead. - let decorate = if attributes.decorations() { - WindowDecorations::RequestServer - } else { - WindowDecorations::RequestClient - }; - - let window = winit.xdg_shell.create_window(surface.clone(), decorate, &winit.queue_handle); - - let viewport = None; - let fractional_scale = None; - - let size = attributes.inner_size(); - - // Create the window with some defaults. - let mut window = Self { - decorate: attributes.decorations(), - min_inner_size: MIN_WINDOW_SIZE, - stateless_size: size.to_logical(1.), - size: size.to_logical(1.), - initial_size: Some(size), - max_inner_size: None, - last_configure: None, - transparent: true, - fractional_scale, - scale_factor: 1., - has_focus: false, - csd_fails: false, - resizable: true, - redraw: false, - frame: None, - theme: None, - compositor, - viewport, - window, - title: Default::default(), - }; - - window.set_title(attributes.title()); - - // Set transparency hint. - window.set_transparent(attributes.transparent); - - window.set_min_inner_size(attributes.min_inner_size()); - window.set_max_inner_size(attributes.max_inner_size()); - - window.set_resizable(attributes.resizable()); - - // window.set_ - - if attributes.maximized() { - window.set_maximized(true); - } - - // TODO: platform attributes. - - // NOTE: initial commit for the window. - window.window.commit(); - - window - } - - /// Set the resizable state on the window. - #[inline] - pub fn set_resizable(&mut self, resizable: bool) { - if self.resizable == resizable { - return; - } - - self.resizable = resizable; - - if resizable { - // Restore min/max sizes of the window. - self.reload_min_max_hints(); - } else { - self.set_min_inner_size(Some(self.size.into())); - self.set_max_inner_size(Some(self.size.into())); - } - - // Reload the state on the frame as well. - if let Some(frame) = self.frame.as_mut() { - frame.set_resizable(resizable); - } - } - - pub fn set_transparent(&mut self, transparent: bool) { - self.transparent = transparent; - self.reload_transparency_hint(); - } - - pub fn set_min_inner_size(&mut self, size: Option) { - let mut size = - size.map(|size| size.to_logical(self.scale_factor)).unwrap_or(MIN_WINDOW_SIZE); - size.height = size.width.max(MIN_WINDOW_SIZE.width); - size.width = size.width.max(MIN_WINDOW_SIZE.width); - // TODO borders - self.window.set_min_size(Some(size.into())); - self.min_inner_size = size; - } - - pub fn set_max_inner_size(&mut self, size: Option) { - let size = size.map(|size| size.to_logical(self.scale_factor)); - self.window.set_max_size(size.map(Into::into)); - self.max_inner_size = size; - } - - pub(crate) fn resize(&mut self, new_size: LogicalSize) { - self.size = new_size; - - // Update the stateless size. - if Some(true) == self.last_configure.as_ref().map(Self::is_stateless) { - self.stateless_size = self.size; - } - - // Update the inner frame. - let ((x, y), outer_size) = if let Some(frame) = self.frame.as_mut() { - // Resize only visible frame. - if !frame.is_hidden() { - frame.resize( - NonZeroU32::new(self.size.width).unwrap(), - NonZeroU32::new(self.size.height).unwrap(), - ); - } - - (frame.location(), frame.add_borders(self.size.width, self.size.height).into()) - } else { - ((0, 0), self.size) - }; - - // Reload the hint. - self.reload_transparency_hint(); - - // Set the window geometry. - self.window.xdg_surface().set_window_geometry( - x, - y, - outer_size.width as i32, - outer_size.height as i32, - ); - - // Update the target viewport, this is used if and only if fractional scaling is - // in use. - if let Some(viewport) = self.viewport.as_ref() { - // Set inner size without the borders. - viewport.set_destination(self.size.width as _, self.size.height as _); + pub(crate) fn configured(&self) -> bool { + match self { + Window::Toplevel(toplevel) => toplevel.configured(), + Window::Popup(popup) => popup.configured(), } } - #[inline] - pub(crate) fn is_stateless(configure: &WindowConfigure) -> bool { - !(configure.is_maximized() || configure.is_fullscreen() || configure.is_tiled()) + pub(crate) fn take_redraw(&mut self) -> bool { + mem::take(match self { + Window::Toplevel(toplevel) => &mut toplevel.redraw, + Window::Popup(popup) => &mut popup.redraw, + }) } - pub(crate) fn set_scale_factor(&mut self, scale_factor: f64) { - self.scale_factor = scale_factor; - - // NOTE: When fractional scaling is not used update the buffer scale. - if self.fractional_scale.is_none() { - let _ = self.window.set_buffer_scale(self.scale_factor as _); - } - - if let Some(frame) = self.frame.as_mut() { - frame.set_scaling_factor(scale_factor); + pub(crate) fn is_xdg_surface(&self) -> bool { + match self { + Window::Toplevel(_) => true, + Window::Popup(_) => true, + #[allow(unreachable_patterns)] + _ => false } } +} - /// Reissue the transparency hint to the compositor. - pub(crate) fn reload_transparency_hint(&self) { - let surface = self.window.wl_surface(); - - if self.transparent { - surface.set_opaque_region(None); - } else if let Ok(region) = Region::new(&*self.compositor) { - region.add(0, 0, i32::MAX, i32::MAX); - surface.set_opaque_region(Some(region.wl_region())); - } else { - // warn!("Failed to mark window opaque."); +impl WaylandSurface for Window { + fn wl_surface(&self) -> &wayland_client::protocol::wl_surface::WlSurface { + match self { + Window::Toplevel(toplevel) => toplevel.window.wl_surface(), + Window::Popup(popup) => popup.popup.wl_surface(), } } - - /// Reload the hints for minimum and maximum sizes. - pub(crate) fn reload_min_max_hints(&mut self) { - self.set_min_inner_size(Some(self.min_inner_size.into())); - self.set_max_inner_size(self.max_inner_size.map(Into::into)); - } - - pub(crate) fn configured(&self) -> bool { - self.last_configure.is_some() - } } -impl CoreWindow for Window { - fn id(&self) -> WindowId { - crate::make_wid(&self.window.wl_surface()) - } - - fn request_redraw(&mut self) { - self.redraw = true; - } - - fn title(&self) -> &str { - &self.title - } - - fn theme(&self) -> Option { - self.theme - } - - fn set_theme(&mut self, theme: Option) { - self.theme = theme; - #[cfg(feature = "sctk-adwaita")] - if let Some(frame) = self.frame.as_mut() { - frame.set_config(into_sctk_adwaita_config(theme)) +impl XdgSurface for Window { + fn xdg_surface(&self) -> &wayland_protocols::xdg::shell::client::xdg_surface::XdgSurface { + match self { + Window::Toplevel(toplevel) => toplevel.window.xdg_surface(), + Window::Popup(popup) => popup.popup.xdg_surface(), + #[allow(unreachable_patterns)] + _ => panic!("Not an XDG surface") + } } +} - fn set_title(&mut self, title: &str) { - let mut title = title.to_owned(); - // Truncate the title to at most 1024 bytes, so that it does not blow up the - // protocol messages - if title.len() > 1024 { - let mut new_len = 1024; - while !title.is_char_boundary(new_len) { - new_len -= 1; - } - title.truncate(new_len); - } - - // Update the CSD title. - if let Some(frame) = self.frame.as_mut() { - frame.set_title(&title); +impl Surface for Window { + fn id(&self) -> winit_core::window::WindowId { + match self { + Window::Toplevel(toplevel) => toplevel.id(), + Window::Popup(popup) => popup.id(), } - - self.window.set_title(&title); - self.title = title; } fn scale_factor(&self) -> f64 { - self.scale_factor - } - - fn inner_size(&self) -> PhysicalSize { - crate::logical_to_physical_rounded(self.size, self.scale_factor) + match self { + Window::Toplevel(toplevel) => toplevel.scale_factor(), + Window::Popup(popup) => popup.scale_factor(), + } } - fn set_minimized(&mut self, minimize: bool) { - if minimize { - self.window.set_minimized(); + fn request_redraw(&mut self) { + match self { + Window::Toplevel(toplevel) => toplevel.request_redraw(), + Window::Popup(popup) => popup.request_redraw(), } } - fn set_maximized(&mut self, maximized: bool) { - if maximized { - self.window.set_maximized(); - } else { - self.window.unset_maximized(); + fn inner_size(&self) -> winit_core::dpi::PhysicalSize { + match self { + Window::Toplevel(toplevel) => toplevel.inner_size(), + Window::Popup(popup) => popup.inner_size(), } } - fn current_monitor(&self) -> Option { - let data = self.window.wl_surface().data::()?; - data.outputs().next().as_ref().map(crate::make_mid) + fn role(&self) -> winit_core::window::WindowRole { + match self { + Window::Toplevel(toplevel) => toplevel.role(), + Window::Popup(popup) => popup.role(), + } } - fn primary_monitor(&self) -> Option { - None + fn role_mut(&mut self) -> winit_core::window::WindowRoleMut { + match self { + Window::Toplevel(toplevel) => toplevel.role_mut(), + Window::Popup(popup) => popup.role_mut(), + } } } impl HasWindowHandle for Window { fn window_handle(&self) -> Result, HandleError> { - let ptr = self.window.wl_surface().id().as_ptr(); - let handle = WaylandWindowHandle::new({ - std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null") - }); - - unsafe { Ok(WindowHandle::borrow_raw(handle.into())) } - } -} - -impl WindowHandler for RuntimeState { - fn request_close(&mut self, _: &Connection, _: &QueueHandle, window: &XdgWindow) { - let window_id = crate::make_wid(window.wl_surface()); - let user_state = self.user.as_mut().unwrap(); - if user_state.close_requested(&mut self.winit, window_id) { - // Instantly drop the window. - drop(self.winit.windows.remove(&window_id)); - user_state.destroyed(&mut self.winit, window_id); - } - } - - fn configure( - &mut self, - _: &Connection, - queue_handle: &QueueHandle, - window: &XdgWindow, - configure: WindowConfigure, - _serial: u32, - ) { - let winit = &mut self.winit; - let window_id = crate::make_wid(window.wl_surface()); - let window = match winit.windows.get_mut(&window_id) { - Some(window) => window, - None => return, - }; - - let scale_factor = window.scale_factor; - - // NOTE: when using fractional scaling or wl_compositor@v6 the scaling - // should be delivered before the first configure, thus apply it to - // properly scale the physical sizes provided by the users. - if let Some(initial_size) = window.initial_size.take() { - window.size = initial_size.to_logical(scale_factor); - window.stateless_size = window.size; - } - - if let Some(subcompositor) = winit.subcompositor.as_ref().filter(|_| { - configure.decoration_mode == DecorationMode::Client - && window.frame.is_none() - && !window.csd_fails - }) { - match WinitFrame::new( - &window.window, - &winit.shm, - #[cfg(feature = "sctk-adwaita")] - winit.compositor.clone(), - subcompositor.clone(), - queue_handle.clone(), - #[cfg(feature = "sctk-adwaita")] - into_sctk_adwaita_config(window.theme), - ) { - Ok(mut frame) => { - frame.set_title(&window.title); - frame.set_scaling_factor(scale_factor); - // Hide the frame if we were asked to not decorate. - frame.set_hidden(!window.decorate); - window.frame = Some(frame); - }, - Err(err) => { - // warn!("Failed to create client side decorations frame: {err}"); - window.csd_fails = true; - }, - } - } else if configure.decoration_mode == DecorationMode::Server { - // Drop the frame for server side decorations to save resources. - window.frame = None; - } - - let (new_size, constrain): (LogicalSize, bool) = match configure.new_size { - (Some(width), Some(height)) => ((width.get(), height.get()).into(), false), - _ => (window.size, true), - }; - - let user = self.user.as_mut().unwrap(); - let initial_configue = window.last_configure.is_none(); - window.last_configure = Some(configure); - - window.resize(new_size); - - if let Some(foo) = self.vtable.foo { - println!("Calling optional method!"); - foo(user); - } - - // NOTE: we consider window as created when its initial configure arrives, until - // then it's considered as not created and attempt to get it will result in - // error. - if initial_configue { - user.created(winit, window_id); - user.scale_factor_changed(winit, window_id, scale_factor); - } - - user.resized(winit, window_id, logical_to_physical_rounded(new_size, scale_factor)); - - if initial_configue { - user.redraw_requested(winit, window_id); + match self { + Window::Toplevel(toplevel) => toplevel.window_handle(), + Window::Popup(popup) => popup.window_handle(), } } } unsafe impl HasRawWindowHandle05 for Window { fn raw_window_handle(&self) -> raw_window_handle_05::RawWindowHandle { - let mut window_handle = raw_window_handle_05::WaylandWindowHandle::empty(); - window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _; - raw_window_handle_05::RawWindowHandle::Wayland(window_handle) - } -} - -#[cfg(feature = "sctk-adwaita")] -fn into_sctk_adwaita_config(theme: Option) -> sctk_adwaita::FrameConfig { - match theme { - Some(Theme::Light) => sctk_adwaita::FrameConfig::light(), - Some(Theme::Dark) => sctk_adwaita::FrameConfig::dark(), - None => sctk_adwaita::FrameConfig::auto(), + match self { + Window::Toplevel(toplevel) => toplevel.raw_window_handle(), + Window::Popup(popup) => popup.raw_window_handle(), + } } -} +} \ No newline at end of file