Skip to main content

surfman/wayland/
connection.rs

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