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