diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index 2112579..af1d194 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc}; use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::EGLDisplay; +use smithay::backend::egl::display::WaylandEGLDisplay; use smithay::{ reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::shm::with_buffer_contents as shm_buffer_contents, @@ -13,14 +13,14 @@ use smithay::{ #[derive(Clone)] pub struct BufferUtils { #[cfg(feature = "egl")] - egl_display: Rc>>, + egl_display: Rc>>, log: Logger, } impl BufferUtils { /// Creates a new `BufferUtils`. #[cfg(feature = "egl")] - pub fn new(egl_display: Rc>>, log: Logger) -> Self { + pub fn new(egl_display: Rc>>, log: Logger) -> Self { Self { egl_display, log } } diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index e924810..19b8c89 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -12,7 +12,7 @@ use glium::{ use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::EGLDisplay; +use smithay::backend::egl::display::WaylandEGLDisplay; use smithay::{ backend::{ egl::{BufferAccessError, EGLImages, Format}, @@ -44,7 +44,7 @@ pub struct GliumDrawer { index_buffer: glium::IndexBuffer, programs: [glium::Program; shaders::FRAGMENT_COUNT], #[cfg(feature = "egl")] - egl_display: Rc>>, + egl_display: Rc>>, log: Logger, } @@ -56,7 +56,11 @@ impl GliumDrawer { impl> + GLGraphicsBackend + 'static> GliumDrawer { #[cfg(feature = "egl")] - pub fn init(backend: T, egl_display: Rc>>, log: Logger) -> GliumDrawer { + pub fn init( + backend: T, + egl_display: Rc>>, + log: Logger, + ) -> GliumDrawer { let display = backend.into(); // building the vertex buffer, which contains all the vertices that we will draw diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index dfc59a5..ed6f9c9 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -15,7 +15,7 @@ use glium::Surface as GliumSurface; use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::{EGLDisplay, EGLGraphicsBackend}; +use smithay::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend}; use smithay::{ backend::{ drm::{ @@ -66,6 +66,7 @@ use crate::glium_drawer::GliumDrawer; use crate::input_handler::AnvilInputHandler; use crate::shell::{init_shell, MyWindowMap, Roles}; use crate::AnvilState; +use smithay::backend::drm::gbm::GbmSurface; pub struct SessionFd(RawFd); impl AsRawFd for SessionFd { @@ -76,8 +77,7 @@ impl AsRawFd for SessionFd { type RenderDevice = EglDevice>, GbmDevice>>; -type RenderSurface = - EglSurface>, GbmDevice>>; +type RenderSurface = EglSurface>>; pub fn run_udev(mut display: Display, mut event_loop: EventLoop, log: Logger) -> Result<(), ()> { let name = display.add_socket_auto().unwrap().into_string().unwrap(); @@ -294,7 +294,7 @@ struct BackendData { struct UdevHandlerImpl { compositor_token: CompositorToken, #[cfg(feature = "egl")] - active_egl_context: Rc>>, + active_egl_context: Rc>>, session: AutoSession, backends: HashMap>, display: Rc>, @@ -313,7 +313,7 @@ impl UdevHandlerImpl { #[cfg(feature = "egl")] pub fn scan_connectors( device: &mut RenderDevice, - egl_display: Rc>>, + egl_display: Rc>>, logger: &::slog::Logger, ) -> HashMap> { // Get a set of all modesetting resource handles (excluding planes): diff --git a/src/backend/drm/egl/mod.rs b/src/backend/drm/egl/mod.rs index 4750d1c..dfb9839 100644 --- a/src/backend/drm/egl/mod.rs +++ b/src/backend/drm/egl/mod.rs @@ -12,20 +12,19 @@ use drm::control::{connector, crtc, encoder, framebuffer, plane, ResourceHandles use drm::SystemError as DrmError; use nix::libc::dev_t; use std::os::unix::io::{AsRawFd, RawFd}; -use std::rc::Rc; #[cfg(feature = "use_system_lib")] use wayland_server::Display; use super::{Device, DeviceHandler, Surface}; -use crate::backend::egl::context::GlAttributes; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; -use crate::backend::egl::EGLContext; use crate::backend::egl::Error as EGLError; #[cfg(feature = "use_system_lib")] -use crate::backend::egl::{EGLDisplay, EGLGraphicsBackend}; +use crate::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend}; mod surface; pub use self::surface::*; +use crate::backend::egl::context::GlAttributes; +use crate::backend::egl::display::EGLDisplay; #[cfg(feature = "backend_session")] pub mod session; @@ -48,7 +47,7 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - dev: Rc>, + dev: EGLDisplay, logger: ::slog::Logger, } @@ -73,31 +72,7 @@ where /// /// Returns an error if the file is no valid device or context /// creation was not successful. - pub fn new(dev: D, logger: L) -> Result::Surface as Surface>::Error>> - where - L: Into>, - { - EglDevice::new_with_gl_attr( - dev, - GlAttributes { - version: None, - profile: None, - debug: cfg!(debug_assertions), - vsync: true, - }, - logger, - ) - } - - /// Create a new [`EglDevice`] from an open device and given [`GlAttributes`] - /// - /// Returns an error if the file is no valid device or context - /// creation was not successful. - pub fn new_with_gl_attr( - mut dev: D, - attributes: GlAttributes, - logger: L, - ) -> Result::Surface as Surface>::Error>> + pub fn new(mut dev: D, logger: L) -> Result::Surface as Surface>::Error>> where L: Into>, { @@ -107,10 +82,7 @@ where debug!(log, "Creating egl context from device"); Ok(EglDevice { - // Open the gbm device from the drm device and create a context based on that - dev: Rc::new( - EGLContext::new(dev, attributes, Default::default(), log.clone()).map_err(Error::EGL)?, - ), + dev: EGLDisplay::new(dev, log.clone()).map_err(Error::EGL)?, logger: log, }) } @@ -147,7 +119,7 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - type Surface = EglSurface; + type Surface = EglSurface<::Surface>; fn device_id(&self) -> dev_t { self.dev.borrow().device_id() @@ -166,15 +138,30 @@ where fn create_surface( &mut self, crtc: crtc::Handle, - ) -> Result, ::Error> { + ) -> Result::Error> { info!(self.logger, "Initializing EglSurface"); - let surface = self.dev.create_surface(crtc).map_err(Error::EGL)?; + // Device trait is unaware of opengl, so using sensible defaults + let attributes = GlAttributes { + version: None, + profile: None, + debug: cfg!(debug_assertions), + vsync: true, + }; + let reqs = Default::default(); - Ok(EglSurface { - dev: self.dev.clone(), - surface, - }) + let context = self.dev.create_context(attributes, reqs).map_err(Error::EGL)?; + let surface = self + .dev + .create_surface( + context.get_pixel_format(), + reqs.double_buffer, + context.get_config_id(), + crtc, + ) + .map_err(Error::EGL)?; + + Ok(EglSurface { context, surface }) } fn process_events(&mut self) { @@ -212,7 +199,7 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - fn bind_wl_display(&self, display: &Display) -> Result { + fn bind_wl_display(&self, display: &Display) -> Result { self.dev.bind_wl_display(display) } } diff --git a/src/backend/drm/egl/surface.rs b/src/backend/drm/egl/surface.rs index 3b84b2e..c896839 100644 --- a/src/backend/drm/egl/surface.rs +++ b/src/backend/drm/egl/surface.rs @@ -1,11 +1,10 @@ use drm::control::{connector, crtc, Mode}; use nix::libc::c_void; -use std::rc::Rc; use super::Error; -use crate::backend::drm::{Device, Surface}; -use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; -use crate::backend::egl::{EGLContext, EGLSurface}; +use crate::backend::drm::Surface; +use crate::backend::egl::native::NativeSurface; +use crate::backend::egl::{get_proc_address, native, EGLContext, EGLSurface}; #[cfg(feature = "renderer_gl")] use crate::backend::graphics::gl::GLGraphicsBackend; #[cfg(feature = "renderer_gl")] @@ -13,24 +12,20 @@ use crate::backend::graphics::PixelFormat; use crate::backend::graphics::{CursorBackend, SwapBuffersError}; /// Egl surface for rendering -pub struct EglSurface +pub struct EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: native::NativeSurface + Surface, { - pub(super) dev: Rc>, - pub(super) surface: EGLSurface, + pub(super) context: EGLContext, + pub(super) surface: EGLSurface, } -impl Surface for EglSurface +impl Surface for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: NativeSurface + Surface, { - type Error = Error<<::Surface as Surface>::Error>; - type Connectors = <::Surface as Surface>::Connectors; + type Connectors = ::Connectors; + type Error = Error<::Error>; fn crtc(&self) -> crtc::Handle { (*self.surface).crtc() @@ -67,14 +62,12 @@ where } } -impl<'a, B, D> CursorBackend<'a> for EglSurface +impl<'a, N> CursorBackend<'a> for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface + CursorBackend<'a>, + N: NativeSurface + Surface + CursorBackend<'a>, { - type CursorFormat = >::CursorFormat; - type Error = >::Error; + type CursorFormat = >::CursorFormat; + type Error = >::Error; fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> { self.surface.set_cursor_position(x, y) @@ -93,18 +86,16 @@ where } #[cfg(feature = "renderer_gl")] -impl GLGraphicsBackend for EglSurface +impl GLGraphicsBackend for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: native::NativeSurface + Surface, { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { self.surface.swap_buffers() } unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - self.dev.get_proc_address(symbol) + get_proc_address(symbol) } fn get_framebuffer_dimensions(&self) -> (u32, u32) { @@ -113,14 +104,14 @@ where } fn is_current(&self) -> bool { - self.dev.is_current() && self.surface.is_current() + self.context.is_current() && self.surface.is_current() } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { - self.surface.make_current() + self.context.make_current_with_surface(&self.surface) } fn get_pixel_format(&self) -> PixelFormat { - self.dev.get_pixel_format() + self.surface.get_pixel_format() } } diff --git a/src/backend/egl/context.rs b/src/backend/egl/context.rs index 5f0d409..be5ad2f 100644 --- a/src/backend/egl/context.rs +++ b/src/backend/egl/context.rs @@ -1,76 +1,38 @@ //! EGL context related structs -use super::{ffi, native, EGLSurface, Error}; -use crate::backend::graphics::PixelFormat; -use nix::libc::{c_int, c_void}; +use super::{ffi, Error}; +use crate::backend::egl::display::EGLDisplay; +use crate::backend::egl::native::NativeSurface; +use crate::backend::egl::{native, EGLSurface}; +use crate::backend::graphics::{PixelFormat, SwapBuffersError}; use slog; -use std::{ - cell::{Ref, RefCell, RefMut}, - ffi::{CStr, CString}, - marker::PhantomData, - mem::MaybeUninit, - ptr, - rc::Rc, -}; +use std::ptr; +use std::sync::{Arc, Weak}; /// EGL context for rendering -pub struct EGLContext> { - native: RefCell, - pub(crate) context: Rc, - pub(crate) display: Rc, - pub(crate) config_id: ffi::egl::types::EGLConfig, - pub(crate) surface_attributes: Vec, +pub struct EGLContext { + context: Arc, + display: Weak, + config_id: ffi::egl::types::EGLConfig, pixel_format: PixelFormat, - pub(crate) wl_drm_support: bool, logger: slog::Logger, - _backend: PhantomData, } -impl> EGLContext { +impl EGLContext { /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay) - pub fn new( - native: N, - attributes: GlAttributes, - reqs: PixelFormatRequirements, - logger: L, - ) -> Result, Error> - where - L: Into>, - { - let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl")); - let ptr = native.ptr()?; - let (context, display, config_id, surface_attributes, pixel_format, wl_drm_support) = - unsafe { EGLContext::::new_internal(ptr, attributes, reqs, log.clone()) }?; - - Ok(EGLContext { - native: RefCell::new(native), - context, - display, - config_id, - surface_attributes, - pixel_format, - wl_drm_support, - logger: log, - _backend: PhantomData, - }) - } - - unsafe fn new_internal( - ptr: ffi::NativeDisplayType, + pub(crate) fn new( + display: &EGLDisplay, mut attributes: GlAttributes, reqs: PixelFormatRequirements, - log: ::slog::Logger, - ) -> Result< - ( - Rc, - Rc, - ffi::egl::types::EGLConfig, - Vec, - PixelFormat, - bool, - ), - Error, - > { + log: L, + ) -> Result + where + L: Into>, + B: native::Backend, + N: native::NativeDisplay, + { + let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl")); + // If no version is given, try OpenGLES 3.0, if available, // fallback to 2.0 otherwise let version = match attributes.version { @@ -79,13 +41,13 @@ impl> EGLContext { None => { debug!(log, "Trying to initialize EGL with OpenGLES 3.0"); attributes.version = Some((3, 0)); - match EGLContext::::new_internal(ptr, attributes, reqs, log.clone()) { + match EGLContext::new(display, attributes, reqs, log.clone()) { Ok(x) => return Ok(x), Err(err) => { warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err); debug!(log, "Trying to initialize EGL with OpenGLES 2.0"); attributes.version = Some((2, 0)); - return EGLContext::::new_internal(ptr, attributes, reqs, log); + return EGLContext::new(display, attributes, reqs, log.clone()); } } } @@ -102,265 +64,11 @@ impl> EGLContext { } }; - ffi::egl::LOAD.call_once(|| { - fn constrain(f: F) -> F - where - F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, - { - f - }; - - ffi::egl::load_with(|sym| { - let name = CString::new(sym).unwrap(); - let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes()); - match symbol { - Ok(x) => *x as *const _, - Err(_) => ptr::null(), - } - }); - let proc_address = constrain(|sym| { - let addr = CString::new(sym).unwrap(); - let addr = addr.as_ptr(); - ffi::egl::GetProcAddress(addr) as *const _ - }); - ffi::egl::load_with(&proc_address); - ffi::egl::BindWaylandDisplayWL::load_with(&proc_address); - ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address); - ffi::egl::QueryWaylandBufferWL::load_with(&proc_address); - }); - - // the first step is to query the list of extensions without any display, if supported - let dp_extensions = { - let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); - - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error - if p.is_null() { - vec![] - } else { - let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - - debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions); - - let display = B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()); - if display == ffi::egl::NO_DISPLAY { - error!(log, "EGL Display is not valid"); - return Err(Error::DisplayNotSupported); - } - - let egl_version = { - let mut major: MaybeUninit = MaybeUninit::uninit(); - let mut minor: MaybeUninit = MaybeUninit::uninit(); - - if ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) == 0 { - return Err(Error::InitFailed); - } - let major = major.assume_init(); - let minor = minor.assume_init(); - - info!(log, "EGL Initialized"); - info!(log, "EGL Version: {:?}", (major, minor)); - - (major, minor) - }; - - // the list of extensions supported by the client once initialized is different from the - // list of extensions obtained earlier - let extensions = if egl_version >= (1, 2) { - let p = CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32)); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } else { - vec![] - }; - - info!(log, "EGL Extensions: {:?}", extensions); - - if egl_version >= (1, 2) && ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - error!(log, "OpenGLES not supported by the underlying EGL implementation"); - return Err(Error::OpenGlesNotSupported); - } - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - if egl_version >= (1, 2) { - trace!(log, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER"); - out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int); - out.push(ffi::egl::RGB_BUFFER as c_int); - } - - trace!(log, "Setting SURFACE_TYPE to WINDOW"); - - out.push(ffi::egl::SURFACE_TYPE as c_int); - // TODO: Some versions of Mesa report a BAD_ATTRIBUTE error - // if we ask for PBUFFER_BIT as well as WINDOW_BIT - out.push((ffi::egl::WINDOW_BIT) as c_int); - - match version { - (3, _) => { - if egl_version < (1, 3) { - error!( - log, - "OpenglES 3.* is not supported on EGL Versions lower then 1.3" - ); - return Err(Error::NoAvailablePixelFormat); - } - trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3"); - out.push(ffi::egl::RENDERABLE_TYPE as c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as c_int); - trace!(log, "Setting CONFORMANT to OPENGL_ES3"); - out.push(ffi::egl::CONFORMANT as c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as c_int); - } - (2, _) => { - if egl_version < (1, 3) { - error!( - log, - "OpenglES 2.* is not supported on EGL Versions lower then 1.3" - ); - return Err(Error::NoAvailablePixelFormat); - } - trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2"); - out.push(ffi::egl::RENDERABLE_TYPE as c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as c_int); - trace!(log, "Setting CONFORMANT to OPENGL_ES2"); - out.push(ffi::egl::CONFORMANT as c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as c_int); - } - (_, _) => unreachable!(), - }; - - if let Some(hardware_accelerated) = reqs.hardware_accelerated { - out.push(ffi::egl::CONFIG_CAVEAT as c_int); - out.push(if hardware_accelerated { - trace!(log, "Setting CONFIG_CAVEAT to NONE"); - ffi::egl::NONE as c_int - } else { - trace!(log, "Setting CONFIG_CAVEAT to SLOW_CONFIG"); - ffi::egl::SLOW_CONFIG as c_int - }); - } - - if let Some(color) = reqs.color_bits { - trace!(log, "Setting RED_SIZE to {}", color / 3); - out.push(ffi::egl::RED_SIZE as c_int); - out.push((color / 3) as c_int); - trace!( - log, - "Setting GREEN_SIZE to {}", - color / 3 + if color % 3 != 0 { 1 } else { 0 } - ); - out.push(ffi::egl::GREEN_SIZE as c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int); - trace!( - log, - "Setting BLUE_SIZE to {}", - color / 3 + if color % 3 == 2 { 1 } else { 0 } - ); - out.push(ffi::egl::BLUE_SIZE as c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int); - } - - if let Some(alpha) = reqs.alpha_bits { - trace!(log, "Setting ALPHA_SIZE to {}", alpha); - out.push(ffi::egl::ALPHA_SIZE as c_int); - out.push(alpha as c_int); - } - - if let Some(depth) = reqs.depth_bits { - trace!(log, "Setting DEPTH_SIZE to {}", depth); - out.push(ffi::egl::DEPTH_SIZE as c_int); - out.push(depth as c_int); - } - - if let Some(stencil) = reqs.stencil_bits { - trace!(log, "Setting STENCIL_SIZE to {}", stencil); - out.push(ffi::egl::STENCIL_SIZE as c_int); - out.push(stencil as c_int); - } - - if let Some(multisampling) = reqs.multisampling { - trace!(log, "Setting SAMPLES to {}", multisampling); - out.push(ffi::egl::SAMPLES as c_int); - out.push(multisampling as c_int); - } - - if reqs.stereoscopy { - error!(log, "Stereoscopy is currently unsupported (sorry!)"); - return Err(Error::NoAvailablePixelFormat); - } - - out.push(ffi::egl::NONE as c_int); - out - }; - - // calling `eglChooseConfig` - let mut config_id = MaybeUninit::uninit(); - let mut num_configs = MaybeUninit::uninit(); - if ffi::egl::ChooseConfig( - display, - descriptor.as_ptr(), - config_id.as_mut_ptr(), - 1, - num_configs.as_mut_ptr(), - ) == 0 - { - return Err(Error::ConfigFailed); - } - - let config_id = config_id.assume_init(); - let num_configs = num_configs.assume_init(); - - if num_configs == 0 { - error!(log, "No matching color format found"); - return Err(Error::NoAvailablePixelFormat); - } - - // analyzing each config - macro_rules! attrib { - ($display:expr, $config:expr, $attr:expr) => {{ - let mut value = MaybeUninit::uninit(); - let res = ffi::egl::GetConfigAttrib( - $display, - $config, - $attr as ffi::egl::types::EGLint, - value.as_mut_ptr(), - ); - if res == 0 { - return Err(Error::ConfigFailed); - } - value.assume_init() - }}; - }; - - let desc = PixelFormat { - hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, - color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8 - + attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8 - + attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8, - alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8, - depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8, - stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8, - stereoscopy: false, - double_buffer: true, - multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) { - 0 | 1 => None, - a => Some(a as u16), - }, - srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that - }; - - info!(log, "Selected color format: {:?}", desc); + let (pixel_format, config_id) = unsafe { display.choose_config(version, reqs)? }; let mut context_attributes = Vec::with_capacity(10); - if egl_version >= (1, 5) || extensions.iter().any(|s| *s == "EGL_KHR_create_context") { + if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") { trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0); context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(version.0 as i32); @@ -368,7 +76,7 @@ impl> EGLContext { context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(version.1 as i32); - if attributes.debug && egl_version >= (1, 5) { + if attributes.debug && display.egl_version >= (1, 5) { trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE"); context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::TRUE as i32); @@ -376,7 +84,7 @@ impl> EGLContext { context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); context_attributes.push(0); - } else if egl_version >= (1, 3) { + } else if display.egl_version >= (1, 3) { trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0); context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(version.0 as i32); @@ -385,74 +93,99 @@ impl> EGLContext { context_attributes.push(ffi::egl::NONE as i32); trace!(log, "Creating EGL context..."); - let context = ffi::egl::CreateContext(display, config_id, ptr::null(), context_attributes.as_ptr()); + // TODO: Support shared contexts + let context = unsafe { + ffi::egl::CreateContext( + *display.display, + config_id, + ptr::null(), + context_attributes.as_ptr(), + ) + }; if context.is_null() { - match ffi::egl::GetError() as u32 { + match unsafe { ffi::egl::GetError() } as u32 { ffi::egl::BAD_ATTRIBUTE => return Err(Error::CreationFailed), err_no => return Err(Error::Unknown(err_no)), } } - debug!(log, "EGL context successfully created"); - - let surface_attributes = { - let mut out: Vec = Vec::with_capacity(3); - - match reqs.double_buffer { - Some(true) => { - trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER"); - out.push(ffi::egl::RENDER_BUFFER as c_int); - out.push(ffi::egl::BACK_BUFFER as c_int); - } - Some(false) => { - trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER"); - out.push(ffi::egl::RENDER_BUFFER as c_int); - out.push(ffi::egl::SINGLE_BUFFER as c_int); - } - None => {} - } - - out.push(ffi::egl::NONE as i32); - out - }; info!(log, "EGL context created"); - // make current and get list of gl extensions - ffi::egl::MakeCurrent(display as *const _, ptr::null(), ptr::null(), context as *const _); - - Ok(( - Rc::new(context as *const _), - Rc::new(display as *const _), + Ok(EGLContext { + context: Arc::new(context as _), + display: Arc::downgrade(&display.display), config_id, - surface_attributes, - desc, - extensions.iter().any(|s| *s == "EGL_WL_bind_wayland_display"), - )) - } - - /// Creates a surface for rendering - pub fn create_surface(&self, args: N::Arguments) -> Result, Error> { - trace!(self.logger, "Creating EGL window surface."); - let surface = self.native.borrow_mut().create_surface(args).map_err(|e| { - error!(self.logger, "EGL surface creation failed: {}", e); - Error::SurfaceCreationFailed - })?; - EGLSurface::new(self, surface).map(|x| { - debug!(self.logger, "EGL surface successfully created"); - x + pixel_format, + logger: log, }) } - /// Returns the address of an OpenGL function. + /// Makes the OpenGL context the current context in the current thread with a surface to + /// read/write to. /// /// # Safety /// - /// The context must have been made current before this function is called. - pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - let addr = CString::new(symbol.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - ffi::egl::GetProcAddress(addr) as *const _ + /// This function is marked unsafe, because the context cannot be made current + /// on multiple threads. + pub unsafe fn make_current_with_surface( + &self, + surface: &EGLSurface, + ) -> ::std::result::Result<(), SwapBuffersError> + where + N: NativeSurface, + { + if let Some(display) = self.display.upgrade() { + let surface_ptr = surface.surface.get(); + + let ret = ffi::egl::MakeCurrent( + (*display) as *const _, + surface_ptr as *const _, + surface_ptr as *const _, + (*self.context) as *const _, + ); + + if ret == 0 { + match ffi::egl::GetError() as u32 { + ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), + err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err), + } + } else { + Ok(()) + } + } else { + Err(SwapBuffersError::ContextLost) + } + } + + /// Makes the OpenGL context the current context in the current thread with no surface bound. + /// + /// # Safety + /// + /// This function is marked unsafe, because the context cannot be made current + /// on multiple threads. + pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { + if let Some(display) = self.display.upgrade() { + let surface_ptr = ptr::null(); + + let ret = ffi::egl::MakeCurrent( + (*display) as *const _, + surface_ptr as *const _, + surface_ptr as *const _, + (*self.context) as *const _, + ); + + if ret == 0 { + match ffi::egl::GetError() as u32 { + ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), + err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err), + } + } else { + Ok(()) + } + } else { + Err(SwapBuffersError::ContextLost) + } } /// Returns true if the OpenGL context is the current one in the thread. @@ -460,41 +193,25 @@ impl> EGLContext { unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ } } + /// Returns the egl config for this context + pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { + self.config_id + } + /// Returns the pixel format of the main framebuffer of the context. pub fn get_pixel_format(&self) -> PixelFormat { self.pixel_format } - - /// Borrow the underlying native display. - /// - /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). - /// Multiple read-only borrows are possible. Borrowing the - /// backend while there is a mutable reference will panic. - pub fn borrow(&self) -> Ref<'_, N> { - self.native.borrow() - } - - /// Borrow the underlying native display mutably. - /// - /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). - /// Holding any other borrow while trying to borrow the backend - /// mutably will panic. Note that EGL will borrow the display - /// mutably during surface creation. - pub fn borrow_mut(&self) -> RefMut<'_, N> { - self.native.borrow_mut() - } } -unsafe impl + Send> Send for EGLContext {} -unsafe impl + Sync> Sync for EGLContext {} - -impl> Drop for EGLContext { +impl Drop for EGLContext { fn drop(&mut self) { unsafe { // we don't call MakeCurrent(0, 0) because we are not sure that the context // is still the current one - ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _); - ffi::egl::Terminate((*self.display) as *const _); + if let Some(display) = self.display.upgrade() { + ffi::egl::DestroyContext((*display) as *const _, (*self.context) as *const _); + } } } } diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs new file mode 100644 index 0000000..10c1870 --- /dev/null +++ b/src/backend/egl/display.rs @@ -0,0 +1,618 @@ +//! Type safe native types for safe egl initialisation + +#[cfg(feature = "use_system_lib")] +use crate::backend::egl::EGLGraphicsBackend; +use crate::backend::egl::{ + ffi, get_proc_address, native, BufferAccessError, EGLContext, EGLImages, EGLSurface, Error, Format, +}; +use std::sync::{Arc, Weak}; + +use std::ptr; + +use nix::libc::{c_int, c_void}; + +#[cfg(feature = "wayland_frontend")] +use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; +#[cfg(feature = "use_system_lib")] +use wayland_sys::server::wl_display; + +use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements}; +#[cfg(feature = "renderer_gl")] +use crate::backend::graphics::gl::ffi as gl_ffi; +use crate::backend::graphics::PixelFormat; +use std::cell::{Ref, RefCell, RefMut}; +use std::ffi::{CStr, CString}; +use std::marker::PhantomData; +use std::mem::MaybeUninit; + +/// [`EGLDisplay`] represents an initialised EGL environment +pub struct EGLDisplay> { + native: RefCell, + pub(crate) display: Arc, + pub(crate) egl_version: (i32, i32), + pub(crate) extensions: Vec, + logger: slog::Logger, + _backend: PhantomData, +} + +impl> EGLDisplay { + /// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay) + pub fn new(native: N, logger: L) -> Result, Error> + where + L: Into>, + { + let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl")); + let ptr = native.ptr()?; + + ffi::egl::LOAD.call_once(|| unsafe { + fn constrain(f: F) -> F + where + F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, + { + f + }; + + ffi::egl::load_with(|sym| { + let name = CString::new(sym).unwrap(); + let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes()); + match symbol { + Ok(x) => *x as *const _, + Err(_) => ptr::null(), + } + }); + let proc_address = constrain(|sym| get_proc_address(sym)); + ffi::egl::load_with(&proc_address); + ffi::egl::BindWaylandDisplayWL::load_with(&proc_address); + ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address); + ffi::egl::QueryWaylandBufferWL::load_with(&proc_address); + }); + + // the first step is to query the list of extensions without any display, if supported + let dp_extensions = unsafe { + let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); + + // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise + // `eglQueryString` returns an error + if p.is_null() { + vec![] + } else { + let p = CStr::from_ptr(p); + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } + }; + + debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions); + + let display = + unsafe { B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()) }; + if display == ffi::egl::NO_DISPLAY { + error!(log, "EGL Display is not valid"); + return Err(Error::DisplayNotSupported); + } + + let egl_version = { + let mut major: MaybeUninit = MaybeUninit::uninit(); + let mut minor: MaybeUninit = MaybeUninit::uninit(); + + if unsafe { ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) } == 0 { + return Err(Error::InitFailed); + } + let major = unsafe { major.assume_init() }; + let minor = unsafe { minor.assume_init() }; + + info!(log, "EGL Initialized"); + info!(log, "EGL Version: {:?}", (major, minor)); + + (major, minor) + }; + + // the list of extensions supported by the client once initialized is different from the + // list of extensions obtained earlier + let extensions = if egl_version >= (1, 2) { + let p = unsafe { CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32)) }; + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } else { + vec![] + }; + + info!(log, "EGL Extensions: {:?}", extensions); + + if egl_version >= (1, 2) && unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) } == 0 { + error!(log, "OpenGLES not supported by the underlying EGL implementation"); + return Err(Error::OpenGlesNotSupported); + } + + Ok(EGLDisplay { + native: RefCell::new(native), + display: Arc::new(display as *const _), + egl_version, + extensions, + logger: log, + _backend: PhantomData, + }) + } + + /// Finds a compatible [`EGLConfig`] for a given set of requirements + pub unsafe fn choose_config( + &self, + version: (u8, u8), + reqs: PixelFormatRequirements, + ) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> { + let descriptor = { + let mut out: Vec = Vec::with_capacity(37); + + if self.egl_version >= (1, 2) { + trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER"); + out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int); + out.push(ffi::egl::RGB_BUFFER as c_int); + } + + trace!(self.logger, "Setting SURFACE_TYPE to WINDOW"); + + out.push(ffi::egl::SURFACE_TYPE as c_int); + // TODO: Some versions of Mesa report a BAD_ATTRIBUTE error + // if we ask for PBUFFER_BIT as well as WINDOW_BIT + out.push((ffi::egl::WINDOW_BIT) as c_int); + + match version { + (3, _) => { + if self.egl_version < (1, 3) { + error!( + self.logger, + "OpenglES 3.* is not supported on EGL Versions lower then 1.3" + ); + return Err(Error::NoAvailablePixelFormat); + } + trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES3"); + out.push(ffi::egl::RENDERABLE_TYPE as c_int); + out.push(ffi::egl::OPENGL_ES3_BIT as c_int); + trace!(self.logger, "Setting CONFORMANT to OPENGL_ES3"); + out.push(ffi::egl::CONFORMANT as c_int); + out.push(ffi::egl::OPENGL_ES3_BIT as c_int); + } + (2, _) => { + if self.egl_version < (1, 3) { + error!( + self.logger, + "OpenglES 2.* is not supported on EGL Versions lower then 1.3" + ); + return Err(Error::NoAvailablePixelFormat); + } + trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES2"); + out.push(ffi::egl::RENDERABLE_TYPE as c_int); + out.push(ffi::egl::OPENGL_ES2_BIT as c_int); + trace!(self.logger, "Setting CONFORMANT to OPENGL_ES2"); + out.push(ffi::egl::CONFORMANT as c_int); + out.push(ffi::egl::OPENGL_ES2_BIT as c_int); + } + (_, _) => unreachable!(), + }; + + if let Some(hardware_accelerated) = reqs.hardware_accelerated { + out.push(ffi::egl::CONFIG_CAVEAT as c_int); + out.push(if hardware_accelerated { + trace!(self.logger, "Setting CONFIG_CAVEAT to NONE"); + ffi::egl::NONE as c_int + } else { + trace!(self.logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG"); + ffi::egl::SLOW_CONFIG as c_int + }); + } + + if let Some(color) = reqs.color_bits { + trace!(self.logger, "Setting RED_SIZE to {}", color / 3); + out.push(ffi::egl::RED_SIZE as c_int); + out.push((color / 3) as c_int); + trace!( + self.logger, + "Setting GREEN_SIZE to {}", + color / 3 + if color % 3 != 0 { 1 } else { 0 } + ); + out.push(ffi::egl::GREEN_SIZE as c_int); + out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int); + trace!( + self.logger, + "Setting BLUE_SIZE to {}", + color / 3 + if color % 3 == 2 { 1 } else { 0 } + ); + out.push(ffi::egl::BLUE_SIZE as c_int); + out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int); + } + + if let Some(alpha) = reqs.alpha_bits { + trace!(self.logger, "Setting ALPHA_SIZE to {}", alpha); + out.push(ffi::egl::ALPHA_SIZE as c_int); + out.push(alpha as c_int); + } + + if let Some(depth) = reqs.depth_bits { + trace!(self.logger, "Setting DEPTH_SIZE to {}", depth); + out.push(ffi::egl::DEPTH_SIZE as c_int); + out.push(depth as c_int); + } + + if let Some(stencil) = reqs.stencil_bits { + trace!(self.logger, "Setting STENCIL_SIZE to {}", stencil); + out.push(ffi::egl::STENCIL_SIZE as c_int); + out.push(stencil as c_int); + } + + if let Some(multisampling) = reqs.multisampling { + trace!(self.logger, "Setting SAMPLES to {}", multisampling); + out.push(ffi::egl::SAMPLES as c_int); + out.push(multisampling as c_int); + } + + if reqs.stereoscopy { + error!(self.logger, "Stereoscopy is currently unsupported (sorry!)"); + return Err(Error::NoAvailablePixelFormat); + } + + out.push(ffi::egl::NONE as c_int); + out + }; + + // calling `eglChooseConfig` + let mut config_id = MaybeUninit::uninit(); + let mut num_configs = MaybeUninit::uninit(); + if ffi::egl::ChooseConfig( + *self.display, + descriptor.as_ptr(), + config_id.as_mut_ptr(), + 1, + num_configs.as_mut_ptr(), + ) == 0 + { + return Err(Error::ConfigFailed); + } + + let config_id = config_id.assume_init(); + let num_configs = num_configs.assume_init(); + + if num_configs == 0 { + error!(self.logger, "No matching color format found"); + return Err(Error::NoAvailablePixelFormat); + } + + // TODO: Filter configs for matching vsync property + + // analyzing each config + macro_rules! attrib { + ($display:expr, $config:expr, $attr:expr) => {{ + let mut value = MaybeUninit::uninit(); + let res = ffi::egl::GetConfigAttrib( + *$display, + $config, + $attr as ffi::egl::types::EGLint, + value.as_mut_ptr(), + ); + if res == 0 { + return Err(Error::ConfigFailed); + } + value.assume_init() + }}; + }; + + let desc = PixelFormat { + hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT) + != ffi::egl::SLOW_CONFIG as i32, + color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8 + + attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8 + + attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8, + alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8, + depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8, + stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8, + stereoscopy: false, + multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) { + 0 | 1 => None, + a => Some(a as u16), + }, + srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that + }; + + info!(self.logger, "Selected color format: {:?}", desc); + + Ok((desc, config_id)) + } + + /// Create a new [`EGLContext`](::backend::egl::EGLContext) + pub fn create_context( + &self, + attributes: GlAttributes, + reqs: PixelFormatRequirements, + ) -> Result { + EGLContext::new(&self, attributes, reqs, self.logger.clone()) + } + + /// Creates a surface for rendering + pub fn create_surface( + &self, + pixel_format: PixelFormat, + double_buffer: Option, + config: ffi::egl::types::EGLConfig, + args: N::Arguments, + ) -> Result, Error> { + trace!(self.logger, "Creating EGL window surface."); + let surface = self.native.borrow_mut().create_surface(args).map_err(|e| { + error!(self.logger, "EGL surface creation failed: {}", e); + Error::SurfaceCreationFailed + })?; + + EGLSurface::new( + &self.display, + pixel_format, + double_buffer, + config, + surface, + self.logger.clone(), + ) + .map(|x| { + debug!(self.logger, "EGL surface successfully created"); + x + }) + } + + /// Returns the runtime egl version of this display + pub fn get_egl_version(&self) -> (i32, i32) { + self.egl_version + } + + /// Returns the supported extensions of this display + pub fn get_extensions(&self) -> Vec { + self.extensions.clone() + } + + /// Borrow the underlying native display. + /// + /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). + /// Multiple read-only borrows are possible. Borrowing the + /// backend while there is a mutable reference will panic. + pub fn borrow(&self) -> Ref<'_, N> { + self.native.borrow() + } + + /// Borrow the underlying native display mutably. + /// + /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). + /// Holding any other borrow while trying to borrow the backend + /// mutably will panic. Note that EGL will borrow the display + /// mutably during surface creation. + pub fn borrow_mut(&self) -> RefMut<'_, N> { + self.native.borrow_mut() + } +} + +impl> Drop for EGLDisplay { + fn drop(&mut self) { + unsafe { + ffi::egl::Terminate((*self.display) as *const _); + } + } +} + +#[cfg(feature = "use_system_lib")] +impl> EGLGraphicsBackend for EGLDisplay { + /// Binds this EGL display to the given Wayland display. + /// + /// This will allow clients to utilize EGL to create hardware-accelerated + /// surfaces. The server will need to be able to handle EGL-[`WlBuffer`]s. + /// + /// ## Errors + /// + /// This might return [`EglExtensionNotSupported`](ErrorKind::EglExtensionNotSupported) + /// if binding is not supported by the EGL implementation. + /// + /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound) + /// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time. + fn bind_wl_display(&self, display: &Display) -> Result { + if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") { + return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); + } + let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) }; + if res == 0 { + return Err(Error::OtherEGLDisplayAlreadyBound); + } + Ok(WaylandEGLDisplay::new( + Arc::downgrade(&self.display), + display.c_ptr(), + &self.extensions, + )) + } +} + +/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s. +/// +/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`]. +#[cfg(feature = "use_system_lib")] +pub struct WaylandEGLDisplay { + display: Weak, + wayland: *mut wl_display, + #[cfg(feature = "renderer_gl")] + gl: gl_ffi::Gles2, + #[cfg(feature = "renderer_gl")] + egl_to_texture_support: bool, +} + +#[cfg(feature = "use_system_lib")] +impl WaylandEGLDisplay { + fn new( + display: Weak, + wayland: *mut wl_display, + extensions: &Vec, + ) -> Self { + #[cfg(feature = "renderer_gl")] + let gl = gl_ffi::Gles2::load_with(|s| unsafe { get_proc_address(s) as *const _ }); + + Self { + display, + wayland, + #[cfg(feature = "renderer_gl")] + egl_to_texture_support: extensions + .iter() + .any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base"), + #[cfg(feature = "renderer_gl")] + gl, + } + } + + /// Try to receive [`EGLImages`] from a given [`WlBuffer`]. + /// + /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) + /// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer + /// to render it another way. + pub fn egl_buffer_contents( + &self, + buffer: WlBuffer, + ) -> ::std::result::Result { + if let Some(display) = self.display.upgrade() { + let mut format: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::EGL_TEXTURE_FORMAT, + &mut format as *mut _, + ) == 0 + } { + return Err(BufferAccessError::NotManaged(buffer)); + } + let format = match format { + x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, + x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA, + ffi::egl::TEXTURE_EXTERNAL_WL => Format::External, + ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV, + ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V, + ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV, + _ => panic!("EGL returned invalid texture type"), + }; + + let mut width: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::WIDTH as i32, + &mut width as *mut _, + ) == 0 + } { + return Err(BufferAccessError::NotManaged(buffer)); + } + + let mut height: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::HEIGHT as i32, + &mut height as *mut _, + ) == 0 + } { + return Err(BufferAccessError::NotManaged(buffer)); + } + + let mut inverted: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::WAYLAND_Y_INVERTED_WL, + &mut inverted as *mut _, + ) != 0 + } { + inverted = 1; + } + + let mut images = Vec::with_capacity(format.num_planes()); + for i in 0..format.num_planes() { + let mut out = Vec::with_capacity(3); + out.push(ffi::egl::WAYLAND_PLANE_WL as i32); + out.push(i as i32); + out.push(ffi::egl::NONE as i32); + + images.push({ + let image = unsafe { + ffi::egl::CreateImageKHR( + *display, + ffi::egl::NO_CONTEXT, + ffi::egl::WAYLAND_BUFFER_WL, + buffer.as_ref().c_ptr() as *mut _, + out.as_ptr(), + ) + }; + if image == ffi::egl::NO_IMAGE_KHR { + return Err(BufferAccessError::EGLImageCreationFailed); + } else { + image + } + }); + } + + Ok(EGLImages { + display: Arc::downgrade(&display), + width: width as u32, + height: height as u32, + y_inverted: inverted != 0, + format, + images, + buffer, + #[cfg(feature = "renderer_gl")] + gl: self.gl.clone(), + #[cfg(feature = "renderer_gl")] + egl_to_texture_support: self.egl_to_texture_support, + }) + } else { + Err(BufferAccessError::ContextLost) + } + } + + /// Try to receive the dimensions of a given [`WlBuffer`]. + /// + /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the + /// context has been lost, `None` is returned. + pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + if let Some(display) = self.display.upgrade() { + let mut width: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::WIDTH as i32, + &mut width as *mut _, + ) == 0 + } { + return None; + } + + let mut height: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::HEIGHT as i32, + &mut height as *mut _, + ) == 0 + } { + return None; + } + + Some((width, height)) + } else { + None + } + } +} + +#[cfg(feature = "use_system_lib")] +impl Drop for WaylandEGLDisplay { + fn drop(&mut self) { + if let Some(display) = self.display.upgrade() { + if !self.wayland.is_null() { + unsafe { + ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _); + } + } + } + } +} diff --git a/src/backend/egl/mod.rs b/src/backend/egl/mod.rs index 6909538..43afc8a 100644 --- a/src/backend/egl/mod.rs +++ b/src/backend/egl/mod.rs @@ -21,28 +21,29 @@ #[cfg(feature = "renderer_gl")] use crate::backend::graphics::gl::ffi as gl_ffi; use nix::libc::c_uint; -use std::{ - ffi::CStr, - fmt, - rc::{Rc, Weak}, -}; +use std::fmt; #[cfg(feature = "wayland_frontend")] use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; -#[cfg(feature = "use_system_lib")] -use wayland_sys::server::wl_display; pub mod context; pub use self::context::EGLContext; mod error; pub use self::error::Error; +use nix::libc::c_void; + #[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)] pub mod ffi; use self::ffi::egl::types::EGLImage; +pub mod display; pub mod native; pub mod surface; pub use self::surface::EGLSurface; +#[cfg(feature = "use_system_lib")] +use crate::backend::egl::display::WaylandEGLDisplay; +use std::ffi::CString; +use std::sync::Weak; /// Error that can happen on optional EGL features #[derive(Debug, Clone, PartialEq)] @@ -61,6 +62,15 @@ impl fmt::Display for EglExtensionNotSupportedError { impl ::std::error::Error for EglExtensionNotSupportedError {} +/// Returns the address of an OpenGL function. +/// +/// Result is independent of displays and does not guarantee an extension is actually supported at runtime. +pub unsafe fn get_proc_address(symbol: &str) -> *const c_void { + let addr = CString::new(symbol.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + ffi::egl::GetProcAddress(addr) as *const _ +} + /// Error that can occur when accessing an EGL buffer #[cfg(feature = "wayland_frontend")] #[derive(thiserror::Error)] @@ -234,7 +244,7 @@ impl Drop for EGLImages { } /// Trait any backend type may implement that allows binding a [`Display`](wayland_server::Display) -/// to create an [`EGLDisplay`] for EGL-based [`WlBuffer`]s. +/// to create an [`WaylandDisplay`](display::WaylandDisplay) for EGL-based [`WlBuffer`]s. #[cfg(feature = "use_system_lib")] pub trait EGLGraphicsBackend { /// Binds this EGL context to the given Wayland display. @@ -249,226 +259,5 @@ pub trait EGLGraphicsBackend { /// /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound) /// if called for the same [`Display`] multiple times, as only one context may be bound at any given time. - fn bind_wl_display(&self, display: &Display) -> Result; -} - -/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s. -/// -/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`]. -#[cfg(feature = "use_system_lib")] -pub struct EGLDisplay { - egl: Weak, - wayland: *mut wl_display, - #[cfg(feature = "renderer_gl")] - gl: gl_ffi::Gles2, - #[cfg(feature = "renderer_gl")] - egl_to_texture_support: bool, -} - -#[cfg(feature = "use_system_lib")] -impl EGLDisplay { - fn new>( - context: &EGLContext, - display: *mut wl_display, - ) -> EGLDisplay { - #[cfg(feature = "renderer_gl")] - let gl = gl_ffi::Gles2::load_with(|s| unsafe { context.get_proc_address(s) as *const _ }); - - EGLDisplay { - egl: Rc::downgrade(&context.display), - wayland: display, - #[cfg(feature = "renderer_gl")] - egl_to_texture_support: { - // the list of gl extensions supported by the context - let data = unsafe { CStr::from_ptr(gl.GetString(gl_ffi::EXTENSIONS) as *const _) } - .to_bytes() - .to_vec(); - let list = String::from_utf8(data).unwrap(); - list.split(' ') - .any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base") - }, - #[cfg(feature = "renderer_gl")] - gl, - } - } - - /// Try to receive [`EGLImages`] from a given [`WlBuffer`]. - /// - /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) - /// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer - /// to render it another way. - pub fn egl_buffer_contents( - &self, - buffer: WlBuffer, - ) -> ::std::result::Result { - if let Some(display) = self.egl.upgrade() { - let mut format: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::EGL_TEXTURE_FORMAT, - &mut format as *mut _, - ) == 0 - } { - return Err(BufferAccessError::NotManaged(buffer)); - } - let format = match format { - x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, - x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA, - ffi::egl::TEXTURE_EXTERNAL_WL => Format::External, - ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV, - ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V, - ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV, - _ => panic!("EGL returned invalid texture type"), - }; - - let mut width: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::WIDTH as i32, - &mut width as *mut _, - ) == 0 - } { - return Err(BufferAccessError::NotManaged(buffer)); - } - - let mut height: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::HEIGHT as i32, - &mut height as *mut _, - ) == 0 - } { - return Err(BufferAccessError::NotManaged(buffer)); - } - - let mut inverted: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::WAYLAND_Y_INVERTED_WL, - &mut inverted as *mut _, - ) != 0 - } { - inverted = 1; - } - - let mut images = Vec::with_capacity(format.num_planes()); - for i in 0..format.num_planes() { - let mut out = Vec::with_capacity(3); - out.push(ffi::egl::WAYLAND_PLANE_WL as i32); - out.push(i as i32); - out.push(ffi::egl::NONE as i32); - - images.push({ - let image = unsafe { - ffi::egl::CreateImageKHR( - *display, - ffi::egl::NO_CONTEXT, - ffi::egl::WAYLAND_BUFFER_WL, - buffer.as_ref().c_ptr() as *mut _, - out.as_ptr(), - ) - }; - if image == ffi::egl::NO_IMAGE_KHR { - return Err(BufferAccessError::EGLImageCreationFailed); - } else { - image - } - }); - } - - Ok(EGLImages { - display: Rc::downgrade(&display), - width: width as u32, - height: height as u32, - y_inverted: inverted != 0, - format, - images, - buffer, - #[cfg(feature = "renderer_gl")] - gl: self.gl.clone(), - #[cfg(feature = "renderer_gl")] - egl_to_texture_support: self.egl_to_texture_support, - }) - } else { - Err(BufferAccessError::ContextLost) - } - } - - /// Try to receive the dimensions of a given [`WlBuffer`]. - /// - /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the - /// context has been lost, `None` is returned. - pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { - if let Some(display) = self.egl.upgrade() { - let mut width: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::WIDTH as i32, - &mut width as *mut _, - ) == 0 - } { - return None; - } - - let mut height: i32 = 0; - if unsafe { - ffi::egl::QueryWaylandBufferWL( - *display, - buffer.as_ref().c_ptr() as *mut _, - ffi::egl::HEIGHT as i32, - &mut height as *mut _, - ) == 0 - } { - return None; - } - - Some((width, height)) - } else { - None - } - } -} - -#[cfg(feature = "use_system_lib")] -impl Drop for EGLDisplay { - fn drop(&mut self) { - if let Some(display) = self.egl.upgrade() { - if !self.wayland.is_null() { - unsafe { - ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _); - } - } - } - } -} - -#[cfg(feature = "use_system_lib")] -impl EGLGraphicsBackend for Rc { - fn bind_wl_display(&self, display: &Display) -> Result { - (**self).bind_wl_display(display) - } -} - -#[cfg(feature = "use_system_lib")] -impl> EGLGraphicsBackend for EGLContext { - fn bind_wl_display(&self, display: &Display) -> Result { - if !self.wl_drm_support { - return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); - } - let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) }; - if res == 0 { - return Err(Error::OtherEGLDisplayAlreadyBound); - } - Ok(EGLDisplay::new(self, display.c_ptr())) - } + fn bind_wl_display(&self, display: &Display) -> Result; } diff --git a/src/backend/egl/surface.rs b/src/backend/egl/surface.rs index c3687d0..05df432 100644 --- a/src/backend/egl/surface.rs +++ b/src/backend/egl/surface.rs @@ -1,21 +1,21 @@ //! EGL surface related structs -use super::{ffi, native, EGLContext, Error}; -use crate::backend::graphics::SwapBuffersError; +use super::{ffi, native, Error}; +use crate::backend::graphics::{PixelFormat, SwapBuffersError}; use nix::libc::c_int; +use std::sync::{Arc, Weak}; use std::{ cell::Cell, ops::{Deref, DerefMut}, - rc::{Rc, Weak}, }; /// EGL surface of a given EGL context for rendering pub struct EGLSurface { - context: Weak, display: Weak, native: N, - surface: Cell, + pub(crate) surface: Cell, config_id: ffi::egl::types::EGLConfig, + pixel_format: PixelFormat, surface_attributes: Vec, } @@ -33,17 +33,42 @@ impl DerefMut for EGLSurface { } impl EGLSurface { - pub(crate) fn new, D: native::NativeDisplay>( - context: &EGLContext, + pub(crate) fn new( + display: &Arc, + pixel_format: PixelFormat, + double_buffered: Option, + config: ffi::egl::types::EGLConfig, native: N, - ) -> Result, Error> { + log: L, + ) -> Result, Error> + where + L: Into>, + { + let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl")); + + let surface_attributes = { + let mut out: Vec = Vec::with_capacity(3); + + match double_buffered { + Some(true) => { + trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER"); + out.push(ffi::egl::RENDER_BUFFER as c_int); + out.push(ffi::egl::BACK_BUFFER as c_int); + } + Some(false) => { + trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER"); + out.push(ffi::egl::RENDER_BUFFER as c_int); + out.push(ffi::egl::SINGLE_BUFFER as c_int); + } + None => {} + } + + out.push(ffi::egl::NONE as i32); + out + }; + let surface = unsafe { - ffi::egl::CreateWindowSurface( - *context.display, - context.config_id, - native.ptr(), - context.surface_attributes.as_ptr(), - ) + ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr()) }; if surface.is_null() { @@ -51,12 +76,12 @@ impl EGLSurface { } Ok(EGLSurface { - context: Rc::downgrade(&context.context), - display: Rc::downgrade(&context.display), + display: Arc::downgrade(display), native, surface: Cell::new(surface), - config_id: context.config_id, - surface_attributes: context.surface_attributes.clone(), + config_id: config, + pixel_format, + surface_attributes, }) } @@ -98,44 +123,22 @@ impl EGLSurface { Ok(()) } - /// Makes the OpenGL context the current context in the current thread. - /// - /// # Safety - /// - /// This function is marked unsafe, because the context cannot be made current - /// on multiple threads. - pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { - if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) { - let ret = ffi::egl::MakeCurrent( - (*display) as *const _, - self.surface.get() as *const _, - self.surface.get() as *const _, - (*context) as *const _, - ); - - if ret == 0 { - match ffi::egl::GetError() as u32 { - ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), - err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err), - } - } else { - Ok(()) - } - } else { - Err(SwapBuffersError::ContextLost) + /// Returns true if the OpenGL surface is the current one in the thread. + pub fn is_current(&self) -> bool { + unsafe { + ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ + && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ } } - /// Returns true if the OpenGL surface is the current one in the thread. - pub fn is_current(&self) -> bool { - if self.context.upgrade().is_some() { - unsafe { - ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ - && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ - } - } else { - false - } + /// Returns the egl config for this context + pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { + self.config_id + } + + /// Returns the pixel format of the main framebuffer of the context. + pub fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format } } diff --git a/src/backend/graphics/format.rs b/src/backend/graphics/format.rs index 96bef69..5262cba 100644 --- a/src/backend/graphics/format.rs +++ b/src/backend/graphics/format.rs @@ -13,8 +13,6 @@ pub struct PixelFormat { pub stencil_bits: u8, /// is stereoscopy enabled pub stereoscopy: bool, - /// is double buffering enabled - pub double_buffer: bool, /// number of samples used for multisampling if enabled pub multisampling: Option, /// is srgb enabled diff --git a/src/backend/winit.rs b/src/backend/winit.rs index f821730..6fd1f64 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,10 +1,9 @@ //! Implementation of backend traits for types provided by `winit` +use crate::backend::egl::display::EGLDisplay; +use crate::backend::egl::get_proc_address; use crate::backend::{ - egl::{ - context::GlAttributes, native, EGLContext, EGLDisplay, EGLGraphicsBackend, EGLSurface, - Error as EGLError, - }, + egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError}, graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError}, input::{ Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, @@ -33,6 +32,9 @@ use winit::{ window::{CursorIcon, Window as WinitWindow, WindowBuilder}, }; +#[cfg(feature = "use_system_lib")] +use crate::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend}; + /// Errors thrown by the `winit` backends #[derive(thiserror::Error, Debug)] pub enum Error { @@ -49,11 +51,13 @@ pub enum Error { enum Window { Wayland { - context: EGLContext, + display: EGLDisplay, + context: EGLContext, surface: EGLSurface, }, X11 { - context: EGLContext, + display: EGLDisplay, + context: EGLContext, surface: EGLSurface, }, } @@ -61,8 +65,8 @@ enum Window { impl Window { fn window(&self) -> Ref<'_, WinitWindow> { match *self { - Window::Wayland { ref context, .. } => context.borrow(), - Window::X11 { ref context, .. } => context.borrow(), + Window::Wayland { ref display, .. } => display.borrow(), + Window::X11 { ref display, .. } => display.borrow(), } } } @@ -158,15 +162,33 @@ where let reqs = Default::default(); let window = Rc::new( if native::NativeDisplay::::is_backend(&winit_window) { - let context = - EGLContext::::new(winit_window, attributes, reqs, log.clone())?; - let surface = context.create_surface(())?; - Window::Wayland { context, surface } + let display = EGLDisplay::::new(winit_window, log.clone())?; + let context = display.create_context(attributes, reqs)?; + let surface = display.create_surface( + context.get_pixel_format(), + reqs.double_buffer, + context.get_config_id(), + (), + )?; + Window::Wayland { + display, + context, + surface, + } } else if native::NativeDisplay::::is_backend(&winit_window) { - let context = - EGLContext::::new(winit_window, attributes, reqs, log.clone())?; - let surface = context.create_surface(())?; - Window::X11 { context, surface } + let display = EGLDisplay::::new(winit_window, log.clone())?; + let context = display.create_context(attributes, reqs)?; + let surface = display.create_surface( + context.get_pixel_format(), + reqs.double_buffer, + context.get_config_id(), + (), + )?; + Window::X11 { + display, + context, + surface, + } } else { return Err(Error::NotSupported); }, @@ -265,10 +287,7 @@ impl GLGraphicsBackend for WinitGraphicsBackend { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { trace!(self.logger, "Getting symbol for {:?}", symbol); - match *self.window { - Window::Wayland { ref context, .. } => context.get_proc_address(symbol), - Window::X11 { ref context, .. } => context.get_proc_address(symbol), - } + get_proc_address(symbol) } fn get_framebuffer_dimensions(&self) -> (u32, u32) { @@ -281,10 +300,12 @@ impl GLGraphicsBackend for WinitGraphicsBackend { Window::Wayland { ref context, ref surface, + .. } => context.is_current() && surface.is_current(), Window::X11 { ref context, ref surface, + .. } => context.is_current() && surface.is_current(), } } @@ -292,24 +313,33 @@ impl GLGraphicsBackend for WinitGraphicsBackend { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { trace!(self.logger, "Setting EGL context to be the current context"); match *self.window { - Window::Wayland { ref surface, .. } => surface.make_current(), - Window::X11 { ref surface, .. } => surface.make_current(), + Window::Wayland { + ref surface, + ref context, + .. + } => context.make_current_with_surface(surface), + Window::X11 { + ref surface, + ref context, + .. + } => context.make_current_with_surface(surface), } } fn get_pixel_format(&self) -> PixelFormat { match *self.window { - Window::Wayland { ref context, .. } => context.get_pixel_format(), - Window::X11 { ref context, .. } => context.get_pixel_format(), + Window::Wayland { ref surface, .. } => surface.get_pixel_format(), + Window::X11 { ref surface, .. } => surface.get_pixel_format(), } } } +#[cfg(feature = "use_system_lib")] impl EGLGraphicsBackend for WinitGraphicsBackend { - fn bind_wl_display(&self, display: &Display) -> Result { + fn bind_wl_display(&self, wl_display: &Display) -> Result { match *self.window { - Window::Wayland { ref context, .. } => context.bind_wl_display(display), - Window::X11 { ref context, .. } => context.bind_wl_display(display), + Window::Wayland { ref display, .. } => display.bind_wl_display(wl_display), + Window::X11 { ref display, .. } => display.bind_wl_display(wl_display), } } }