surfman/platform/unix/wayland/
connection.rs

1// surfman/surfman/src/platform/unix/wayland/connection.rs
2//
3//! A wrapper for Wayland connections (displays).
4
5use super::device::{Adapter, Device, NativeDevice};
6use super::surface::NativeWidget;
7use crate::egl;
8use crate::egl::types::{EGLAttrib, EGLDisplay};
9use crate::info::GLApi;
10use crate::platform::generic::egl::device::EGL_FUNCTIONS;
11use crate::platform::generic::egl::ffi::EGL_PLATFORM_WAYLAND_KHR;
12use crate::Error;
13
14use euclid::default::Size2D;
15use std::os::raw::c_void;
16use std::ptr;
17use std::sync::Arc;
18use wayland_sys::client::{wayland_client_handle, wl_display, wl_proxy};
19
20/// A connection to the Wayland server.
21#[derive(Clone)]
22pub struct Connection {
23    pub(crate) native_connection: Arc<NativeConnectionWrapper>,
24}
25
26pub(crate) struct NativeConnectionWrapper {
27    pub(crate) egl_display: EGLDisplay,
28    wayland_display: Option<*mut wl_display>,
29}
30
31/// An EGL display wrapping a Wayland display.
32pub struct NativeConnection(pub EGLDisplay);
33
34unsafe impl Send for Connection {}
35
36impl Connection {
37    /// Connects to the default Wayland server.
38    #[inline]
39    pub fn new() -> Result<Connection, Error> {
40        unsafe {
41            let wayland_display = (wayland_client_handle().wl_display_connect)(ptr::null());
42            Connection::from_wayland_display(wayland_display, true)
43        }
44    }
45
46    /// Wraps an existing EGL display in a `Connection`.
47    ///
48    /// The display is not retained, as there is no way to do this in the EGL API. Therefore, it is
49    /// the caller's responsibility to ensure that the EGL display remains alive as long as the
50    /// connection is.
51    pub unsafe fn from_native_connection(
52        native_connection: NativeConnection,
53    ) -> Result<Connection, Error> {
54        Connection::from_egl_display(native_connection.0, None)
55    }
56
57    /// Returns the underlying native connection.
58    #[inline]
59    pub fn native_connection(&self) -> NativeConnection {
60        NativeConnection(self.native_connection.egl_display)
61    }
62
63    /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES).
64    #[inline]
65    pub fn gl_api(&self) -> GLApi {
66        if std::env::var("SURFMAN_FORCE_GLES").is_ok() {
67            GLApi::GLES
68        } else {
69            GLApi::GL
70        }
71    }
72
73    /// Returns the "best" adapter on this system, preferring high-performance hardware adapters.
74    ///
75    /// This is an alias for `Connection::create_hardware_adapter()`.
76    #[inline]
77    pub fn create_adapter(&self) -> Result<Adapter, Error> {
78        self.create_hardware_adapter()
79    }
80
81    /// Returns the "best" adapter on this system, preferring high-performance hardware adapters.
82    #[inline]
83    pub fn create_hardware_adapter(&self) -> Result<Adapter, Error> {
84        Ok(Adapter::hardware())
85    }
86
87    /// Returns the "best" adapter on this system, preferring low-power hardware adapters.
88    #[inline]
89    pub fn create_low_power_adapter(&self) -> Result<Adapter, Error> {
90        Ok(Adapter::low_power())
91    }
92
93    /// Returns the "best" adapter on this system, preferring software adapters.
94    #[inline]
95    pub fn create_software_adapter(&self) -> Result<Adapter, Error> {
96        Ok(Adapter::software())
97    }
98
99    /// Opens the hardware device corresponding to the given adapter.
100    ///
101    /// Device handles are local to a single thread.
102    #[inline]
103    pub fn create_device(&self, adapter: &Adapter) -> Result<Device, Error> {
104        Device::new(self, adapter)
105    }
106
107    /// Opens the hardware device corresponding to the adapter wrapped in the given native
108    /// device.
109    ///
110    /// This is present for compatibility with other backends.
111    #[inline]
112    pub unsafe fn create_device_from_native_device(
113        &self,
114        native_device: NativeDevice,
115    ) -> Result<Device, Error> {
116        Device::new(self, &native_device.adapter)
117    }
118
119    unsafe fn from_wayland_display(
120        wayland_display: *mut wl_display,
121        is_owned: bool,
122    ) -> Result<Connection, Error> {
123        if wayland_display.is_null() {
124            return Err(Error::ConnectionFailed);
125        }
126
127        EGL_FUNCTIONS.with(|egl| {
128            let display_attributes = [egl::NONE as EGLAttrib];
129            let egl_display = egl.GetPlatformDisplay(
130                EGL_PLATFORM_WAYLAND_KHR,
131                wayland_display as *mut c_void,
132                display_attributes.as_ptr(),
133            );
134            if egl_display == egl::NO_DISPLAY {
135                return Err(Error::DeviceOpenFailed);
136            }
137
138            let (mut egl_major_version, mut egl_minor_version) = (0, 0);
139            let ok = egl.Initialize(egl_display, &mut egl_major_version, &mut egl_minor_version);
140            assert_ne!(ok, egl::FALSE);
141
142            let owned_display = if is_owned {
143                Some(wayland_display)
144            } else {
145                None
146            };
147            Connection::from_egl_display(egl_display, owned_display)
148        })
149    }
150
151    fn from_egl_display(
152        egl_display: EGLDisplay,
153        wayland_display: Option<*mut wl_display>,
154    ) -> Result<Connection, Error> {
155        Ok(Connection {
156            native_connection: Arc::new(NativeConnectionWrapper {
157                egl_display,
158                wayland_display,
159            }),
160        })
161    }
162
163    /// Opens the display connection corresponding to the given `RawDisplayHandle`.
164    #[cfg(feature = "sm-raw-window-handle-05")]
165    pub fn from_raw_display_handle(
166        raw_handle: rwh_05::RawDisplayHandle,
167    ) -> Result<Connection, Error> {
168        use rwh_05::RawDisplayHandle::Wayland;
169        use rwh_05::WaylandDisplayHandle;
170        unsafe {
171            let wayland_display = match raw_handle {
172                Wayland(WaylandDisplayHandle { display, .. }) => display as *mut wl_display,
173                _ => return Err(Error::IncompatibleRawDisplayHandle),
174            };
175
176            Connection::from_wayland_display(wayland_display, false)
177        }
178    }
179
180    /// Opens the display connection corresponding to the given `DisplayHandle`.
181    #[cfg(feature = "sm-raw-window-handle-06")]
182    pub fn from_display_handle(handle: rwh_06::DisplayHandle) -> Result<Connection, Error> {
183        use rwh_06::RawDisplayHandle::Wayland;
184        use rwh_06::WaylandDisplayHandle;
185        unsafe {
186            let wayland_display = match handle.as_raw() {
187                Wayland(WaylandDisplayHandle { display, .. }) => {
188                    display.as_ptr() as *mut wl_display
189                }
190                _ => return Err(Error::IncompatibleRawDisplayHandle),
191            };
192
193            Connection::from_wayland_display(wayland_display, false)
194        }
195    }
196
197    /// Create a native widget from a raw pointer
198    pub unsafe fn create_native_widget_from_ptr(
199        &self,
200        raw: *mut c_void,
201        size: Size2D<i32>,
202    ) -> NativeWidget {
203        NativeWidget {
204            wayland_surface: raw as *mut wl_proxy,
205            size,
206        }
207    }
208
209    /// Creates a native widget type from the given `RawWindowHandle`
210    #[cfg(feature = "sm-raw-window-handle-05")]
211    pub fn create_native_widget_from_raw_window_handle(
212        &self,
213        raw_handle: rwh_05::RawWindowHandle,
214        window_size: Size2D<i32>,
215    ) -> Result<NativeWidget, Error> {
216        use rwh_05::RawWindowHandle::Wayland;
217
218        let wayland_surface = match raw_handle {
219            Wayland(handle) => handle.surface as *mut wl_proxy,
220            _ => return Err(Error::IncompatibleNativeWidget),
221        };
222
223        Ok(NativeWidget {
224            wayland_surface,
225            size: window_size,
226        })
227    }
228
229    /// Creates a native widget type from the given `WindowHandle`
230    #[cfg(feature = "sm-raw-window-handle-06")]
231    pub fn create_native_widget_from_window_handle(
232        &self,
233        handle: rwh_06::WindowHandle,
234        window_size: Size2D<i32>,
235    ) -> Result<NativeWidget, Error> {
236        use rwh_06::RawWindowHandle::Wayland;
237
238        let wayland_surface = match handle.as_raw() {
239            Wayland(handle) => handle.surface.as_ptr() as *mut wl_proxy,
240            _ => return Err(Error::IncompatibleNativeWidget),
241        };
242
243        Ok(NativeWidget {
244            wayland_surface,
245            size: window_size,
246        })
247    }
248}
249
250impl Drop for NativeConnectionWrapper {
251    fn drop(&mut self) {
252        unsafe {
253            if let Some(wayland_display) = self.wayland_display {
254                (wayland_client_handle().wl_display_disconnect)(wayland_display);
255            }
256        }
257    }
258}
259
260impl NativeConnection {
261    /// Returns the current native connection, if applicable.
262    #[inline]
263    pub fn current() -> Result<NativeConnection, Error> {
264        unsafe {
265            EGL_FUNCTIONS.with(|egl| {
266                let display = egl.GetCurrentDisplay();
267                if display != egl::NO_DISPLAY {
268                    Ok(NativeConnection(display))
269                } else {
270                    Err(Error::NoCurrentConnection)
271                }
272            })
273        }
274    }
275}