wayland_egl/
lib.rs

1#![warn(missing_docs, missing_debug_implementations)]
2#![forbid(improper_ctypes, unsafe_op_in_unsafe_fn)]
3
4//! EGL utilities
5//!
6//! This module contains bindings to the `libwayland-egl.so` library.
7//!
8//! This library is used to interface with the OpenGL stack, and creating
9//! EGL surfaces from a wayland surface.
10//!
11//! See [`WlEglSurface`] documentation for details.
12
13use std::{fmt, os::raw::c_void};
14
15use wayland_backend::client::ObjectId;
16use wayland_sys::{client::wl_proxy, egl::*, ffi_dispatch};
17
18/// Checks if the wayland-egl lib is available and can be used
19///
20/// Trying to create an [`WlEglSurface`] while this function returns
21/// [`false`] will result in a panic.
22pub fn is_available() -> bool {
23    is_lib_available()
24}
25
26/// EGL surface
27///
28/// This object is a simple wrapper around a `wl_surface` to add the EGL
29/// capabilities. Just use the [`ptr()`][WlEglSurface::ptr()] method once this object
30/// is created to get the window pointer your OpenGL library is needing to initialize
31/// the EGL context (you'll most likely need the display ptr as well, that you can
32/// get via the [`ObjectId::as_ptr()`] method on of the `wl_display` ID).
33#[derive(Debug)]
34pub struct WlEglSurface {
35    ptr: *mut wl_egl_window,
36}
37
38impl WlEglSurface {
39    /// Create an EGL surface from a wayland surface
40    ///
41    /// This method will check that the provided [`ObjectId`] is still alive and from the
42    /// correct interface (`wl_surface`).
43    ///
44    /// You must always destroy the [`WlEglSurface`] *before* the underling `wl_surface`
45    /// protocol object.
46    pub fn new(surface: ObjectId, width: i32, height: i32) -> Result<Self, Error> {
47        if surface.interface().name != "wl_surface" {
48            return Err(Error::InvalidId);
49        }
50
51        let ptr = surface.as_ptr();
52        if ptr.is_null() {
53            // ObjectId::as_ptr() returns NULL if the surface is no longer alive
54            Err(Error::InvalidId)
55        } else {
56            // SAFETY: We are sure the pointer is valid and the interface is correct.
57            unsafe { Self::new_from_raw(ptr, width, height) }
58        }
59    }
60
61    /// Create an EGL surface from a raw pointer to a wayland surface.
62    ///
63    /// # Safety
64    ///
65    /// The provided pointer must be a valid `wl_surface` pointer from `libwayland-client`.
66    pub unsafe fn new_from_raw(
67        surface: *mut wl_proxy,
68        width: i32,
69        height: i32,
70    ) -> Result<Self, Error> {
71        if width <= 0 || height <= 0 {
72            return Err(Error::InvalidSize);
73        }
74        let ptr = ffi_dispatch!(wayland_egl_handle(), wl_egl_window_create, surface, width, height);
75        if ptr.is_null() {
76            panic!("egl window allocation failed");
77        }
78        Ok(Self { ptr })
79    }
80
81    /// Fetch current size of the EGL surface
82    pub fn get_size(&self) -> (i32, i32) {
83        let mut w = 0i32;
84        let mut h = 0i32;
85        unsafe {
86            ffi_dispatch!(
87                wayland_egl_handle(),
88                wl_egl_window_get_attached_size,
89                self.ptr,
90                &mut w as *mut i32,
91                &mut h as *mut i32
92            );
93        }
94        (w, h)
95    }
96
97    /// Resize the EGL surface
98    ///
99    /// The two first arguments `(width, height)` are the new size of
100    /// the surface, the two others `(dx, dy)` represent the displacement
101    /// of the top-left corner of the surface. It allows you to control the
102    /// direction of the resizing if necessary.
103    pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) {
104        unsafe {
105            ffi_dispatch!(
106                wayland_egl_handle(),
107                wl_egl_window_resize,
108                self.ptr,
109                width,
110                height,
111                dx,
112                dy
113            )
114        }
115    }
116
117    /// Raw pointer to the EGL surface
118    ///
119    /// You'll need this pointer to initialize the EGL context in your
120    /// favourite OpenGL lib.
121    pub fn ptr(&self) -> *const c_void {
122        self.ptr as *const c_void
123    }
124}
125
126// SAFETY: We own the pointer to the wl_egl_window and can therefore be transferred to another thread.
127unsafe impl Send for WlEglSurface {}
128// Note that WlEglSurface is !Sync. This is because the pointer performs no internal synchronization.
129
130impl Drop for WlEglSurface {
131    fn drop(&mut self) {
132        unsafe {
133            ffi_dispatch!(wayland_egl_handle(), wl_egl_window_destroy, self.ptr);
134        }
135    }
136}
137
138/// EGL surface creation error.
139#[derive(Debug)]
140pub enum Error {
141    /// Surface width or height are <= 0.
142    InvalidSize,
143    /// Passed surface object is not a surface.
144    InvalidId,
145}
146
147impl std::error::Error for Error {}
148
149impl fmt::Display for Error {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        match self {
152            Error::InvalidSize => write!(f, "surface width or height is <= 0"),
153            Error::InvalidId => write!(f, "object id is not a surface"),
154        }
155    }
156}