diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 5b5e7c6e69..8360548acd 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -51,3 +51,6 @@ wayland-protocols.workspace = true wayland-backend = { version = "0.3.3", features = ["client_system"] } wayland-client = { version = "0.31.2" } wayland-sys = { version = "0.31.1", features = ["dlopen"] } +as-raw-xcb-connection = "1.0.1" +tiny-xlib = "0.2.3" +x11rb = { version = "0.13.1", features = ["allow-unsafe-code", "dl-libxcb", "dri3"] } \ No newline at end of file diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs index 8af4797bc2..6c6dd24184 100644 --- a/wgpu/src/window.rs +++ b/wgpu/src/window.rs @@ -2,6 +2,8 @@ pub mod compositor; #[cfg(all(unix, not(target_os = "macos")))] mod wayland; +#[cfg(all(unix, not(target_os = "macos")))] +mod x11; pub use compositor::Compositor; pub use wgpu::Surface; diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 15919db5e7..c0839451ac 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -8,6 +8,8 @@ use crate::{Backend, Primitive, Renderer, Settings}; #[cfg(all(unix, not(target_os = "macos")))] use super::wayland::get_wayland_device_ids; +#[cfg(all(unix, not(target_os = "macos")))] +use super::x11::get_x11_device_ids; /// A window graphics backend for iced powered by `wgpu`. #[allow(missing_debug_implementations)] @@ -29,7 +31,10 @@ impl Compositor { compatible_window: Option, ) -> Option { #[cfg(all(unix, not(target_os = "macos")))] - let ids = compatible_window.as_ref().and_then(get_wayland_device_ids); + let ids = compatible_window.as_ref().and_then(|window| { + get_wayland_device_ids(window) + .or_else(|| get_x11_device_ids(window)) + }); // HACK: // 1. If we specifically didn't select an nvidia gpu diff --git a/wgpu/src/window/x11.rs b/wgpu/src/window/x11.rs new file mode 100644 index 0000000000..9661b39c8b --- /dev/null +++ b/wgpu/src/window/x11.rs @@ -0,0 +1,77 @@ +use as_raw_xcb_connection::AsRawXcbConnection; +use raw_window_handle::{ + RawDisplayHandle, XcbDisplayHandle, XlibDisplayHandle, +}; +use rustix::fs::{fstat, major, minor}; +use tiny_xlib::Display; +use x11rb::{ + connection::RequestConnection, + protocol::extensions::dri3::{ + ConnectionExt as _, X11_EXTENSION_NAME as DRI3_NAME, + }, + xcb_ffi::XCBConnection, +}; + +pub fn get_x11_device_ids(window: &W) -> Option<(u16, u16)> { + x11rb::xcb_ffi::load_libxcb().ok()?; + + let (conn, screen) = match window + .display_handle() + .map(|handle| handle.as_raw()) + { + #[allow(unsafe_code)] + Ok(RawDisplayHandle::Xlib(XlibDisplayHandle { + display, + screen, + .. + })) => match display { + Some(ptr) => unsafe { + let xlib_display = + tiny_xlib::Display::from_ptr(ptr.as_ptr()).ok()?; + let conn = XCBConnection::from_raw_xcb_connection( + xlib.as_raw_xcb_connection(), + false, + ) + .ok(); + // intentially leak the display, we don't want to close the connection + std::mem::forget(xlib_display); + + (conn?, screen) + }, + None => (XCBConnection::connect(None), screen), + }, + Ok(RawDisplayHandle::Xcb(XcbDisplayHandle { + connection, + screen, + .. + })) => match connection { + Some(ptr) => ( + unsafe { + XCBConnection::from_raw_xcb_connection(ptr.as_ptr(), false) + .ok()? + }, + screen, + ), + None => (XCBConnection::connect(None), screen), + }, + _ => { + return None; + } + }; + + // check for DRI3 + let _ = conn.extension_information(DRI3_NAME).ok()??; + // we have dri3, dri3_open exists on any version, so lets skip version checks. + + // provider being NONE tells the X server to use the RandR provider. + let screen = &conn.setup().roots[screen]; + let dri3 = connection + .dri3_open(screen.root, x11rb::NONE)? + .reply() + .ok()?; + let device_fd = dri3.device_fd; + let stat = fstat(file).ok()?; + let dev = stat.st_rdev; + + super::ids_from_dev(dev) +}