diff --git a/examples/window.rs b/examples/window.rs index 76c60c39e8..7496176fd7 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,10 +1,12 @@ //! Simple winit window example. +use std::any::Any; use std::error::Error; use winit::application::ApplicationHandler; use winit::event::WindowEvent; -use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::event_loop::{ActiveEventLoop, AsWaylandEventLoop, EventLoop}; +use winit::platform::wayland::LinuxDrm; #[cfg(web_platform)] use winit::platform::web::WindowAttributesWeb; use winit::window::{Window, WindowAttributes, WindowId}; @@ -19,6 +21,12 @@ struct App { window: Option>, } +impl LinuxDrm for App { + fn main_device(&mut self, device: libc::dev_t) { + dbg!(device); + } +} + impl ApplicationHandler for App { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { #[cfg(not(web_platform))] @@ -67,6 +75,10 @@ impl ApplicationHandler for App { _ => (), } } + + fn as_any(&mut self) -> Option<&mut dyn Any> { + Some(self) + } } fn main() -> Result<(), Box> { @@ -75,7 +87,10 @@ fn main() -> Result<(), Box> { tracing::init(); - let event_loop = EventLoop::new()?; + let mut event_loop = EventLoop::new()?; + if let Some(event_loop) = event_loop.as_wayland_event_loop() { + event_loop.listen_linux_dmabuf::(); + } // For alternative loop run options see `pump_events` and `run_on_demand` examples. event_loop.run_app(App::default())?; diff --git a/winit-core/src/application/mod.rs b/winit-core/src/application/mod.rs index b9fd34bff1..8af0b1149f 100644 --- a/winit-core/src/application/mod.rs +++ b/winit-core/src/application/mod.rs @@ -1,5 +1,7 @@ //! End user application handling. +use std::any::Any; + use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; use crate::event_loop::ActiveEventLoop; use crate::window::WindowId; @@ -343,6 +345,11 @@ pub trait ApplicationHandler { fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { None } + + #[inline(always)] + fn as_any(&mut self) -> Option<&mut dyn Any> { + None + } } #[deny(clippy::missing_trait_methods)] @@ -411,6 +418,11 @@ impl ApplicationHandler for &mut A { fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { (**self).macos_handler() } + + #[inline(always)] + fn as_any(&mut self) -> Option<&mut dyn Any> { + (**self).as_any() + } } #[deny(clippy::missing_trait_methods)] @@ -479,4 +491,9 @@ impl ApplicationHandler for Box { fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { (**self).macos_handler() } + + #[inline(always)] + fn as_any(&mut self) -> Option<&mut dyn Any> { + (**self).as_any() + } } diff --git a/winit-wayland/src/event_loop/mod.rs b/winit-wayland/src/event_loop/mod.rs index ee6b4e8e92..33e432c804 100644 --- a/winit-wayland/src/event_loop/mod.rs +++ b/winit-wayland/src/event_loop/mod.rs @@ -12,6 +12,7 @@ use std::time::{Duration, Instant}; use calloop::ping::Ping; use dpi::LogicalSize; +use libc::dev_t; use rustix::event::{PollFd, PollFlags}; use rustix::pipe::{self, PipeFlags}; use sctk::reexports::calloop_wayland_source::WaylandSource; @@ -29,6 +30,7 @@ use winit_core::event_loop::{ use winit_core::monitor::MonitorHandle as CoreMonitorHandle; use winit_core::window::Theme; +use crate::state::ExtensionEvents; use crate::types::cursor::WaylandCustomCursor; mod proxy; @@ -60,6 +62,7 @@ pub struct EventLoop { buffer_sink: EventSink, compositor_updates: Vec, window_ids: Vec, + extension_events: Vec, /// The Wayland dispatcher to has raw access to the queue when needed, such as /// when creating a new window. @@ -69,7 +72,7 @@ pub struct EventLoop { handle: Arc, /// Event loop window target. - active_event_loop: ActiveEventLoop, + pub(crate) active_event_loop: ActiveEventLoop, // XXX drop after everything else, just to be safe. /// Calloop's event loop. @@ -95,7 +98,7 @@ impl EventLoop { let event_loop = calloop::EventLoop::::try_new().map_err(|err| os_error!(err))?; - let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())?; + let mut winit_state = WinitState::new(globals, &queue_handle, event_loop.handle())?; // NOTE: do a roundtrip after binding the globals to prevent potential // races with the server. @@ -108,7 +111,8 @@ impl EventLoop { let result = queue.dispatch_pending(winit_state); if result.is_ok() && (!winit_state.events_sink.is_empty() - || !winit_state.window_compositor_updates.is_empty()) + || !winit_state.window_compositor_updates.is_empty() + || !winit_state.extension_events.is_empty()) { winit_state.dispatched_events = true; } @@ -152,10 +156,12 @@ impl EventLoop { control_flow: Cell::new(ControlFlow::default()), exit: Cell::new(None), state: RefCell::new(winit_state), + extensions: Default::default(), }; let event_loop = Self { loop_running: false, + extension_events: Vec::new(), compositor_updates: Vec::new(), buffer_sink: EventSink::default(), window_ids: Vec::new(), @@ -324,6 +330,7 @@ impl EventLoop { // when finished. let mut compositor_updates = std::mem::take(&mut self.compositor_updates); let mut buffer_sink = std::mem::take(&mut self.buffer_sink); + let mut extension_events = std::mem::take(&mut self.extension_events); let mut window_ids = std::mem::take(&mut self.window_ids); app.new_events(&self.active_event_loop, cause); @@ -339,6 +346,15 @@ impl EventLoop { app.proxy_wake_up(&self.active_event_loop); } + if let Some(main_device) = self.active_event_loop.extensions.main_device { + self.with_state(|state| extension_events.append(&mut state.extension_events)); + + for extension_event in extension_events.drain(..) { + let ExtensionEvents::LinuxMainDevice(device) = extension_event; + main_device(app, device); + } + } + // Drain the pending compositor updates. self.with_state(|state| compositor_updates.append(&mut state.window_compositor_updates)); @@ -606,6 +622,13 @@ pub struct ActiveEventLoop { /// Handle for the underlying event loop. pub handle: Arc, + + pub(crate) extensions: Extensions, +} + +#[derive(Default, Debug)] +pub(crate) struct Extensions { + pub(crate) main_device: Option, } impl RootActiveEventLoop for ActiveEventLoop { diff --git a/winit-wayland/src/lib.rs b/winit-wayland/src/lib.rs index 1a35a3de22..b92b10231f 100644 --- a/winit-wayland/src/lib.rs +++ b/winit-wayland/src/lib.rs @@ -17,10 +17,12 @@ use std::ffi::c_void; use std::ptr::NonNull; use dpi::{LogicalSize, PhysicalSize}; +use libc::dev_t; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::Proxy; use sctk::shm::slot::{Buffer, CreateBufferError, SlotPool}; use wayland_client::protocol::wl_shm::Format; +use winit_core::application::ApplicationHandler; use winit_core::event_loop::ActiveEventLoop as CoreActiveEventLoop; use winit_core::window::{ ActivationToken, PlatformWindowAttributes, Window as CoreWindow, WindowId, @@ -42,6 +44,33 @@ mod window; pub use self::event_loop::{ActiveEventLoop, EventLoop}; pub use self::window::Window; +/// Listen to Linux Direct Rendering Manager (DRM) events. +pub trait LinuxDrm: ApplicationHandler { + /// Returns the device that the system prefers to use. + /// + /// The EGL EGL_EXT_device_drm and Vulkan VK_EXT_physical_device_drm extensions can be + /// used to select a matching device for accelerated rendering. + /// + /// This function returns `None` if the device is unknown. + fn main_device(&mut self, device: dev_t); +} + +impl EventLoop { + pub fn listen_linux_dmabuf(&mut self) { + self.active_event_loop.extensions.main_device = + Some(|app: &mut dyn ApplicationHandler, device: dev_t| { + app.as_any() + .expect("as_any not implemented") + .downcast_mut::() + .unwrap() + .main_device(device); + }); + + let queue_handle = &self.active_event_loop.queue_handle; + self.active_event_loop.state.get_mut().register_linux_dmabuf(queue_handle); + } +} + /// Additional methods on [`ActiveEventLoop`] that are specific to Wayland. pub trait ActiveEventLoopExtWayland { /// True if the [`ActiveEventLoop`] uses Wayland. diff --git a/winit-wayland/src/state.rs b/winit-wayland/src/state.rs index 9f8a4f490a..dd6a2d7835 100644 --- a/winit-wayland/src/state.rs +++ b/winit-wayland/src/state.rs @@ -3,6 +3,7 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use ahash::AHashMap; +use libc::dev_t; use sctk::compositor::{CompositorHandler, CompositorState}; use sctk::output::{OutputHandler, OutputState}; use sctk::reexports::calloop::LoopHandle; @@ -33,12 +34,15 @@ use crate::types::wp_fractional_scaling::FractionalScalingManager; use crate::types::wp_viewporter::ViewporterState; use crate::types::xdg_activation::XdgActivationState; use crate::types::xdg_toplevel_icon_manager::XdgToplevelIconManagerState; +use crate::types::zwp_linux_dmabuf::LinuxDmabufManager; use crate::window::{WindowRequests, WindowState}; use crate::WindowId; /// Winit's Wayland state. #[derive(Debug)] pub struct WinitState { + globals: GlobalList, + /// The WlRegistry. pub registry_state: RegistryState, @@ -112,6 +116,9 @@ pub struct WinitState { /// KWin blur manager. pub kwin_blur_manager: Option, + /// Dmabuf manager. + pub linux_dmabuf_manager: Option, + /// Loop handle to re-register event sources, such as keyboard repeat. pub loop_handle: LoopHandle<'static, Self>, @@ -121,20 +128,27 @@ pub struct WinitState { /// Whether the user initiated a wake up. pub proxy_wake_up: bool, + + pub extension_events: Vec, +} + +#[derive(Debug, Clone, Copy)] +pub enum ExtensionEvents { + LinuxMainDevice(dev_t), } impl WinitState { pub fn new( - globals: &GlobalList, + globals: GlobalList, queue_handle: &QueueHandle, loop_handle: LoopHandle<'static, WinitState>, ) -> Result { - let registry_state = RegistryState::new(globals); + let registry_state = RegistryState::new(&globals); let compositor_state = - CompositorState::bind(globals, queue_handle).map_err(|err| os_error!(err))?; + CompositorState::bind(&globals, queue_handle).map_err(|err| os_error!(err))?; let subcompositor_state = match SubcompositorState::bind( compositor_state.wl_compositor().clone(), - globals, + &globals, queue_handle, ) { Ok(c) => Some(c), @@ -144,10 +158,10 @@ impl WinitState { }, }; - let output_state = OutputState::new(globals, queue_handle); + let output_state = OutputState::new(&globals, queue_handle); let monitors = output_state.outputs().map(MonitorHandle::new).collect(); - let seat_state = SeatState::new(globals, queue_handle); + let seat_state = SeatState::new(&globals, queue_handle); let mut seats = AHashMap::default(); for seat in seat_state.seats() { @@ -155,13 +169,13 @@ impl WinitState { } let (viewporter_state, fractional_scaling_manager) = - if let Ok(fsm) = FractionalScalingManager::new(globals, queue_handle) { - (ViewporterState::new(globals, queue_handle).ok(), Some(fsm)) + if let Ok(fsm) = FractionalScalingManager::new(&globals, queue_handle) { + (ViewporterState::new(&globals, queue_handle).ok(), Some(fsm)) } else { (None, None) }; - let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?; + let shm = Shm::bind(&globals, queue_handle).map_err(|err| os_error!(err))?; let image_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap())); Ok(Self { @@ -172,9 +186,9 @@ impl WinitState { seat_state, shm, - xdg_shell: XdgShell::bind(globals, queue_handle).map_err(|err| os_error!(err))?, - xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(), - xdg_toplevel_icon_manager: XdgToplevelIconManagerState::bind(globals, queue_handle) + xdg_shell: XdgShell::bind(&globals, queue_handle).map_err(|err| os_error!(err))?, + xdg_activation: XdgActivationState::bind(&globals, queue_handle).ok(), + xdg_toplevel_icon_manager: XdgToplevelIconManagerState::bind(&globals, queue_handle) .ok(), image_pool, @@ -185,13 +199,14 @@ impl WinitState { window_events_sink: Default::default(), viewporter_state, fractional_scaling_manager, - kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(), + linux_dmabuf_manager: None, + kwin_blur_manager: KWinBlurManager::new(&globals, queue_handle).ok(), seats, - text_input_state: TextInputState::new(globals, queue_handle).ok(), + text_input_state: TextInputState::new(&globals, queue_handle).ok(), - relative_pointer: RelativePointerState::new(globals, queue_handle).ok(), - pointer_constraints: PointerConstraintsState::new(globals, queue_handle) + relative_pointer: RelativePointerState::new(&globals, queue_handle).ok(), + pointer_constraints: PointerConstraintsState::new(&globals, queue_handle) .map(Arc::new) .ok(), pointer_surfaces: Default::default(), @@ -202,9 +217,17 @@ impl WinitState { // Make it true by default. dispatched_events: true, proxy_wake_up: false, + extension_events: Vec::new(), + globals, }) } + pub fn register_linux_dmabuf(&mut self, queue_handle: &QueueHandle) { + if self.linux_dmabuf_manager.is_none() { + self.linux_dmabuf_manager = LinuxDmabufManager::new(&self.globals, queue_handle).ok(); + } + } + pub fn scale_factor_changed( &mut self, surface: &WlSurface, diff --git a/winit-wayland/src/types/mod.rs b/winit-wayland/src/types/mod.rs index e27a98ff2b..40e95651cb 100644 --- a/winit-wayland/src/types/mod.rs +++ b/winit-wayland/src/types/mod.rs @@ -6,3 +6,4 @@ pub mod wp_fractional_scaling; pub mod wp_viewporter; pub mod xdg_activation; pub mod xdg_toplevel_icon_manager; +pub mod zwp_linux_dmabuf; diff --git a/winit-wayland/src/types/zwp_linux_dmabuf.rs b/winit-wayland/src/types/zwp_linux_dmabuf.rs new file mode 100644 index 0000000000..b9dca8e192 --- /dev/null +++ b/winit-wayland/src/types/zwp_linux_dmabuf.rs @@ -0,0 +1,77 @@ +use libc::dev_t; +use sctk::globals::GlobalData; +use wayland_client::globals::{BindError, GlobalList}; +use wayland_client::{delegate_dispatch, Connection, Dispatch, QueueHandle}; +use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1; +use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1; +use wayland_protocols::wp::linux_dmabuf::zv1::client::{ + zwp_linux_dmabuf_feedback_v1, zwp_linux_dmabuf_v1, +}; + +use crate::state::{ExtensionEvents, WinitState}; + +#[derive(Debug)] +pub struct LinuxDmabufManager { + _manager: ZwpLinuxDmabufV1, + feedback: LinuxDmabufFeedback, +} + +#[derive(Debug)] +pub struct LinuxDmabufFeedback { + _feedback: ZwpLinuxDmabufFeedbackV1, + pending_device: Option, +} + +impl LinuxDmabufManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + let manager: ZwpLinuxDmabufV1 = globals.bind(queue_handle, 4..=5, GlobalData)?; + let feedback = manager.get_default_feedback(queue_handle, GlobalData); + let feedback = LinuxDmabufFeedback { _feedback: feedback, pending_device: None }; + Ok(Self { _manager: manager, feedback }) + } +} + +impl Dispatch for LinuxDmabufManager { + fn event( + _state: &mut WinitState, + _proxy: &ZwpLinuxDmabufV1, + _event: zwp_linux_dmabuf_v1::Event, + _data: &GlobalData, + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + // nothing + } +} + +impl Dispatch for LinuxDmabufManager { + fn event( + state: &mut WinitState, + _proxy: &ZwpLinuxDmabufFeedbackV1, + event: zwp_linux_dmabuf_feedback_v1::Event, + _data: &GlobalData, + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + use zwp_linux_dmabuf_feedback_v1::Event; + match event { + Event::Done => { + let manager = state.linux_dmabuf_manager.as_mut().unwrap(); + if let Some(device) = manager.feedback.pending_device.take() { + state.extension_events.push(ExtensionEvents::LinuxMainDevice(device)); + } + }, + Event::MainDevice { device } => { + let dev = dev_t::from_ne_bytes(device.try_into().unwrap()); + state.linux_dmabuf_manager.as_mut().unwrap().feedback.pending_device = Some(dev); + }, + _ => {}, + } + } +} + +delegate_dispatch!(WinitState: [ZwpLinuxDmabufV1: GlobalData] => LinuxDmabufManager); +delegate_dispatch!(WinitState: [ZwpLinuxDmabufFeedbackV1: GlobalData] => LinuxDmabufManager); diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index fc3e15ad4f..e7e3ce69fc 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -255,6 +255,21 @@ impl HasDisplayHandle for EventLoop { } } +#[cfg(wayland_platform)] +pub trait AsWaylandEventLoop { + fn as_wayland_event_loop(&mut self) -> Option<&mut crate::platform::wayland::EventLoop>; +} + +#[cfg(wayland_platform)] +impl AsWaylandEventLoop for EventLoop { + fn as_wayland_event_loop(&mut self) -> Option<&mut crate::platform::wayland::EventLoop> { + match &mut self.event_loop { + platform_impl::EventLoop::Wayland(event_loop) => Some(event_loop), + _ => None, + } + } +} + #[cfg(any(x11_platform, wayland_platform))] impl AsFd for EventLoop { /// Get the underlying [EventLoop]'s `fd` which you can register