surfman/platform/unix/x11/
connection.rs

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