diff --git a/winit-wayland/src/event_loop/mod.rs b/winit-wayland/src/event_loop/mod.rs index ee6b4e8e92..77d53d404c 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; @@ -566,6 +567,10 @@ impl EventLoop { fn exit_code(&self) -> Option { self.active_event_loop.exit_code() } + + pub fn main_drm_device(&self) -> Option { + self.active_event_loop.state.borrow().linux_dmabuf_manager.as_ref()?.device() + } } impl AsFd for EventLoop { diff --git a/winit-wayland/src/state.rs b/winit-wayland/src/state.rs index 9f8a4f490a..66716642b5 100644 --- a/winit-wayland/src/state.rs +++ b/winit-wayland/src/state.rs @@ -33,6 +33,7 @@ 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; @@ -112,6 +113,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>, @@ -161,6 +165,8 @@ impl WinitState { (None, None) }; + let linux_dmabuf_manager = LinuxDmabufManager::new(globals, queue_handle).ok(); + 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())); @@ -186,6 +192,7 @@ impl WinitState { viewporter_state, fractional_scaling_manager, kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(), + linux_dmabuf_manager, seats, text_input_state: TextInputState::new(globals, queue_handle).ok(), 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..50bf26fd89 --- /dev/null +++ b/winit-wayland/src/types/zwp_linux_dmabuf.rs @@ -0,0 +1,83 @@ +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::WinitState; + +#[derive(Debug)] +pub struct LinuxDmabufManager { + _manager: ZwpLinuxDmabufV1, + feedback: LinuxDmabufFeedback, +} + +#[derive(Debug)] +pub struct LinuxDmabufFeedback { + _feedback: ZwpLinuxDmabufFeedbackV1, + device: Option, + 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, device: None, pending_device: None }; + Ok(Self { _manager: manager, feedback }) + } + + pub fn device(&self) -> Option { + self.feedback.device + } +} + +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() { + manager.feedback.device = Some(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/changelog/unreleased.md b/winit/src/changelog/unreleased.md index e255263da4..9bf7cdd468 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -83,6 +83,8 @@ changelog entry. - Each platform now has corresponding `WindowAttributes` struct instead of trait extension. - On Wayland, added implementation for `Window::set_window_icon` - Add `Window::request_ime_update` to atomically apply set of IME changes. +- Add `EventLoopExtDrm` to query the main GPU device on multi-GPU systems on + Linux. ### Changed diff --git a/winit/src/platform/drm.rs b/winit/src/platform/drm.rs new file mode 100644 index 0000000000..77a0621aa3 --- /dev/null +++ b/winit/src/platform/drm.rs @@ -0,0 +1,21 @@ +use libc::dev_t; + +use crate::event_loop::EventLoop; + +/// Additional methods on [`EventLoop`] that are specific to platforms using the Linux +/// Direct Rendering Manager (DRM). +pub trait EventLoopExtDrm { + /// 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_drm_device(&self) -> Option; +} + +impl EventLoopExtDrm for EventLoop { + fn main_drm_device(&self) -> Option { + self.event_loop.main_drm_device() + } +} diff --git a/winit/src/platform/mod.rs b/winit/src/platform/mod.rs index 34efd0ffcf..c1a58e7a44 100644 --- a/winit/src/platform/mod.rs +++ b/winit/src/platform/mod.rs @@ -37,6 +37,8 @@ pub use winit_web as web; pub use winit_win32 as windows; #[cfg(x11_platform)] pub use winit_x11 as x11; +#[cfg(any(x11_platform, wayland_platform))] +pub mod drm; #[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, docsrs))] pub mod scancode; diff --git a/winit/src/platform_impl/linux/mod.rs b/winit/src/platform_impl/linux/mod.rs index 47e6652054..cb19315d5e 100644 --- a/winit/src/platform_impl/linux/mod.rs +++ b/winit/src/platform_impl/linux/mod.rs @@ -7,6 +7,7 @@ use std::env; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::time::Duration; +use libc::dev_t; pub(crate) use winit_common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey}; use winit_core::application::ApplicationHandler; use winit_core::error::{EventLoopError, NotSupportedError}; @@ -169,6 +170,15 @@ impl EventLoop { pub fn window_target(&self) -> &dyn ActiveEventLoop { x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target()) } + + pub fn main_drm_device(&self) -> Option { + match self { + #[cfg(wayland_platform)] + EventLoop::Wayland(evlp) => evlp.main_drm_device(), + #[cfg(x11_platform)] + EventLoop::X(_) => None, + } + } } impl AsFd for EventLoop {