Skip to main content

surfman/x11/
connection.rs

1//! A wrapper for X11 server connections (`DISPLAY` variables).
2
3use super::device::{Device, NativeDevice};
4use super::surface::NativeWidget;
5use crate::base::egl::device::EGL_FUNCTIONS;
6use crate::base::egl::ffi::EGL_PLATFORM_X11_KHR;
7use crate::egl;
8use crate::egl::types::{EGLAttrib, EGLDisplay};
9use crate::error::Error;
10use crate::info::GLApi;
11use crate::mesa_surfaceless::device::Adapter;
12
13use euclid::default::Size2D;
14
15use std::marker::PhantomData;
16use std::os::raw::c_void;
17use std::ptr;
18use std::sync::{Arc, Once};
19use x11_dl::xlib::{Display, Xlib};
20
21static X_THREADS_INIT: Once = Once::new();
22
23/// A connection to the X11 display server.
24#[derive(Clone)]
25pub struct Connection {
26    pub(crate) native_connection: Arc<NativeConnectionWrapper>,
27}
28
29unsafe impl Send for Connection {}
30
31pub(crate) struct NativeConnectionWrapper {
32    pub(crate) xlib: Xlib,
33    pub(crate) egl_display: EGLDisplay,
34    x11_display: *mut Display,
35    /// Whether or not this [`NativeConnectionWrapper`] created its X11 [`Display`].
36    /// If true, the `Drop` handler is responsible for cleaning up both the X11
37    /// and [`EGLDisplay`].
38    is_owned: bool,
39}
40
41/// Wrapper for an X11 and EGL display.
42#[derive(Clone)]
43pub struct NativeConnection {
44    /// The EGL display associated with that X11 display.
45    ///
46    /// You can obtain this with `eglGetPlatformDisplay()`.
47    ///
48    /// It is assumed that this EGL display is already initialized, via `eglInitialize()`.
49    pub egl_display: EGLDisplay,
50    /// The corresponding Xlib Display. This must be present; do not pass NULL.
51    pub x11_display: *mut Display,
52}
53
54impl Drop for NativeConnectionWrapper {
55    #[inline]
56    fn drop(&mut self) {
57        unsafe {
58            if self.is_owned {
59                terminate_egl_display(self.egl_display);
60                (self.xlib.XCloseDisplay)(self.x11_display);
61            }
62            self.x11_display = ptr::null_mut();
63        }
64    }
65}
66
67impl Connection {
68    /// Connects to the default display.
69    #[inline]
70    pub fn new() -> Result<Connection, Error> {
71        unsafe {
72            let xlib = Xlib::open().map_err(|_| Error::ConnectionFailed)?;
73
74            X_THREADS_INIT.call_once(|| {
75                (xlib.XInitThreads)();
76            });
77
78            let x11_display = (xlib.XOpenDisplay)(ptr::null());
79            if x11_display.is_null() {
80                return Err(Error::ConnectionFailed);
81            }
82
83            let egl_display = create_egl_display(x11_display);
84
85            Ok(Connection {
86                native_connection: Arc::new(NativeConnectionWrapper {
87                    xlib,
88                    egl_display,
89                    x11_display,
90                    is_owned: true,
91                }),
92            })
93        }
94    }
95
96    /// Wraps an existing X11 `Display` in a `Connection`.
97    ///
98    /// # Safety
99    ///
100    /// Before calling this function, X11 must have be initialized in a thread-safe
101    /// manner by using `XInitThreads()`. Otherwise, it will not be safe to use `surfman` from
102    /// multiple threads.
103    ///
104    /// The display is not retained, as there is no way to do that in the X11 API. Therefore, it is
105    /// the caller's responsibility to ensure that the display connection is not closed before this
106    /// `Connection` object is disposed of.
107    #[inline]
108    pub unsafe fn from_native_connection(
109        native_connection: NativeConnection,
110    ) -> Result<Connection, Error> {
111        let xlib = Xlib::open().map_err(|_| Error::ConnectionFailed)?;
112        Ok(Connection {
113            native_connection: Arc::new(NativeConnectionWrapper {
114                xlib,
115                egl_display: native_connection.egl_display,
116                x11_display: native_connection.x11_display,
117                is_owned: false,
118            }),
119        })
120    }
121
122    fn from_x11_display(x11_display: *mut Display) -> Result<Connection, Error> {
123        let xlib = Xlib::open().map_err(|_| Error::ConnectionFailed)?;
124        unsafe {
125            let egl_display = create_egl_display(x11_display);
126            Ok(Connection {
127                native_connection: Arc::new(NativeConnectionWrapper {
128                    xlib,
129                    egl_display,
130                    x11_display,
131                    is_owned: false,
132                }),
133            })
134        }
135    }
136
137    /// Returns the underlying native connection.
138    #[inline]
139    pub fn native_connection(&self) -> NativeConnection {
140        NativeConnection {
141            egl_display: self.native_connection.egl_display,
142            x11_display: self.native_connection.x11_display,
143        }
144    }
145
146    /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES).
147    #[inline]
148    pub fn gl_api(&self) -> GLApi {
149        GLApi::GL
150    }
151
152    /// Returns the "best" adapter on this system, preferring high-performance hardware adapters.
153    ///
154    /// This is an alias for `Connection::create_hardware_adapter()`.
155    #[inline]
156    pub fn create_adapter(&self) -> Result<Adapter, Error> {
157        self.create_hardware_adapter()
158    }
159
160    /// Returns the "best" adapter on this system, preferring high-performance hardware adapters.
161    #[inline]
162    pub fn create_hardware_adapter(&self) -> Result<Adapter, Error> {
163        Ok(Adapter::hardware())
164    }
165
166    /// Returns the "best" adapter on this system, preferring low-power hardware adapters.
167    #[inline]
168    pub fn create_low_power_adapter(&self) -> Result<Adapter, Error> {
169        Ok(Adapter::low_power())
170    }
171
172    /// Returns the "best" adapter on this system, preferring software adapters.
173    #[inline]
174    pub fn create_software_adapter(&self) -> Result<Adapter, Error> {
175        Ok(Adapter::software())
176    }
177
178    /// Opens the hardware device corresponding to the given adapter.
179    ///
180    /// Device handles are local to a single thread.
181    #[inline]
182    pub fn create_device(&self, adapter: &Adapter) -> Result<Device, Error> {
183        Device::new(self, adapter)
184    }
185
186    /// Opens the hardware device corresponding to the adapter wrapped in the given native
187    /// device.
188    ///
189    /// This is present for compatibility with other backends.
190    #[inline]
191    pub unsafe fn create_device_from_native_device(
192        &self,
193        native_device: NativeDevice,
194    ) -> Result<Device, Error> {
195        Device::new(self, &native_device.adapter)
196    }
197
198    /// Opens the display connection corresponding to the given `DisplayHandle`.
199    pub fn from_display_handle(
200        handle: raw_window_handle::DisplayHandle,
201    ) -> Result<Connection, Error> {
202        use raw_window_handle::RawDisplayHandle::Xcb;
203        use raw_window_handle::RawDisplayHandle::Xlib;
204        use raw_window_handle::XlibDisplayHandle;
205        let display = match handle.as_raw() {
206            Xlib(XlibDisplayHandle {
207                display: Some(display),
208                ..
209            }) => display.as_ptr() as *mut Display,
210            Xcb(_) => return Err(Error::Unimplemented),
211            _ => return Err(Error::IncompatibleRawDisplayHandle),
212        };
213
214        Connection::from_x11_display(display)
215    }
216
217    /// Create a native widget from a raw pointer
218    pub unsafe fn create_native_widget_from_ptr(
219        &self,
220        raw: *mut c_void,
221        _size: Size2D<i32>,
222    ) -> NativeWidget {
223        NativeWidget {
224            window: std::mem::transmute(raw),
225        }
226    }
227
228    /// Create a native widget type from the given `WindowHandle`.
229    pub fn create_native_widget_from_window_handle(
230        &self,
231        handle: raw_window_handle::WindowHandle,
232        _size: Size2D<i32>,
233    ) -> Result<NativeWidget, Error> {
234        use raw_window_handle::RawWindowHandle::Xlib;
235
236        match handle.as_raw() {
237            Xlib(handle) => Ok(NativeWidget {
238                window: handle.window,
239            }),
240            _ => Err(Error::IncompatibleNativeWidget),
241        }
242    }
243}
244
245impl NativeConnectionWrapper {
246    #[inline]
247    pub(crate) fn lock_display(&self) -> DisplayGuard<'_> {
248        unsafe {
249            let display = self.x11_display;
250            let xlib = &self.xlib;
251            (xlib.XLockDisplay)(display);
252            DisplayGuard {
253                xlib,
254                display,
255                phantom: PhantomData,
256            }
257        }
258    }
259}
260
261pub(crate) struct DisplayGuard<'a> {
262    xlib: &'a Xlib,
263    display: *mut Display,
264    phantom: PhantomData<&'a ()>,
265}
266
267impl<'a> Drop for DisplayGuard<'a> {
268    fn drop(&mut self) {
269        unsafe {
270            (self.xlib.XUnlockDisplay)(self.display);
271        }
272    }
273}
274
275impl<'a> DisplayGuard<'a> {
276    #[inline]
277    pub(crate) fn display(&self) -> *mut Display {
278        self.display
279    }
280}
281
282unsafe fn create_egl_display(display: *mut Display) -> EGLDisplay {
283    EGL_FUNCTIONS.with(|egl| {
284        let display_attributes = [egl::NONE as EGLAttrib];
285        let egl_display = egl.GetPlatformDisplay(
286            EGL_PLATFORM_X11_KHR,
287            display as *mut c_void,
288            display_attributes.as_ptr(),
289        );
290
291        let (mut egl_major_version, mut egl_minor_version) = (0, 0);
292        let ok = egl.Initialize(egl_display, &mut egl_major_version, &mut egl_minor_version);
293        assert_ne!(ok, egl::FALSE);
294
295        egl_display
296    })
297}
298
299unsafe fn terminate_egl_display(display: EGLDisplay) {
300    EGL_FUNCTIONS.with(|egl| {
301        let ok = egl.Terminate(display);
302        debug_assert_ne!(ok, egl::FALSE);
303    })
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[cfg_attr(not(feature = "sm-test"), test)]
311    pub fn test_from_x11_display() {
312        use crate::{ContextAttributeFlags, ContextAttributes, GLVersion};
313
314        let connection = Connection::new().unwrap();
315        let adapter = connection.create_low_power_adapter().unwrap();
316        let device = match connection.create_device(&adapter) {
317            Ok(device) => device,
318            Err(Error::RequiredExtensionUnavailable) => {
319                // Can't run this test on this hardware.
320                return;
321            }
322            Err(error) => panic!("Failed to create device: {error:?}"),
323        };
324
325        let context_descriptor = device
326            .create_context_descriptor(&ContextAttributes {
327                version: GLVersion::new(3, 0),
328                flags: ContextAttributeFlags::empty(),
329            })
330            .unwrap();
331
332        let mut context = device.create_context(&context_descriptor, None).unwrap();
333        device.make_context_current(&context).unwrap();
334
335        {
336            let _second_connection =
337                Connection::from_x11_display(connection.native_connection().x11_display).unwrap();
338        }
339
340        device.destroy_context(&mut context).unwrap();
341    }
342}