From 7db239744c1cbd0245c9ba4b391671f4f7c10d1b Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 17 Jun 2022 20:56:40 +0300 Subject: [PATCH] WIP: Decouple glutin from winit Thit commit removes direct dependency on the winit and using raw_window_handle crate instead --- glutin/Cargo.toml | 66 +- glutin/src/api/dlloader.rs | 69 - glutin/src/api/egl/config.rs | 295 ++++ glutin/src/api/egl/context.rs | 186 +++ glutin/src/api/egl/display.rs | 220 +++ glutin/src/api/egl/make_current_guard.rs | 98 -- glutin/src/api/egl/mod.rs | 1357 +------------------ glutin/src/api/egl/surface.rs | 364 +++++ glutin/src/api/glx/config.rs | 250 ++++ glutin/src/api/glx/context.rs | 176 +++ glutin/src/api/glx/display.rs | 106 ++ glutin/src/api/glx/make_current_guard.rs | 81 -- glutin/src/api/glx/mod.rs | 862 ++---------- glutin/src/api/glx/surface.rs | 191 +++ glutin/src/api/ios/mod.rs | 454 ------- glutin/src/api/mod.rs | 6 +- glutin/src/api/osmesa/mod.rs | 224 --- glutin/src/api/wgl/make_current_guard.rs | 54 - glutin/src/api/wgl/mod.rs | 918 +------------ glutin/src/config.rs | 352 +++++ glutin/src/context.rs | 402 +++--- glutin/src/display.rs | 170 +++ glutin/src/error.rs | 160 +++ glutin/src/lib.rs | 719 +--------- glutin/src/lib_loading.rs | 46 + glutin/src/platform/android.rs | 23 - glutin/src/platform/ios.rs | 21 - glutin/src/platform/macos.rs | 22 - glutin/src/platform/mod.rs | 55 - glutin/src/platform/unix.rs | 32 - glutin/src/platform/windows.rs | 25 - glutin/src/platform_impl/android/mod.rs | 169 +-- glutin/src/platform_impl/ios/mod.rs | 4 - glutin/src/platform_impl/linux/mod.rs | 572 ++++++++ glutin/src/platform_impl/linux/x11.rs | 74 + glutin/src/platform_impl/macos/helpers.rs | 132 -- glutin/src/platform_impl/macos/mod.rs | 357 ----- glutin/src/platform_impl/mod.rs | 23 - glutin/src/platform_impl/unix/mod.rs | 471 ------- glutin/src/platform_impl/unix/wayland.rs | 195 --- glutin/src/platform_impl/unix/x11.rs | 674 --------- glutin/src/platform_impl/unix/x11/utils.rs | 70 - glutin/src/platform_impl/windows/mod.rs | 548 ++++---- glutin/src/surface.rs | 284 ++++ glutin/src/windowed.rs | 353 ----- glutin_egl_sys/src/lib.rs | 1 + glutin_examples/Cargo.toml | 4 +- glutin_examples/build.rs | 6 +- glutin_examples/examples/buffer_age.rs | 40 - glutin_examples/examples/damage.rs | 92 -- glutin_examples/examples/fullscreen.rs | 117 -- glutin_examples/examples/headless.rs | 151 --- glutin_examples/examples/multiwindow.rs | 65 - glutin_examples/examples/raw_context.rs | 130 -- glutin_examples/examples/sharing.rs | 150 -- glutin_examples/examples/support/mod.rs | 355 ----- glutin_examples/examples/transparent.rs | 41 - glutin_examples/examples/window.rs | 107 +- glutin_examples/ios-example/rust/src/lib.rs | 55 +- glutin_gles2_sys/src/lib.rs | 1 + glutin_glx_sys/src/lib.rs | 17 + glutin_wgl_sys/src/lib.rs | 1 + rustfmt.toml | 2 +- 63 files changed, 4307 insertions(+), 8958 deletions(-) delete mode 100644 glutin/src/api/dlloader.rs create mode 100644 glutin/src/api/egl/config.rs create mode 100644 glutin/src/api/egl/context.rs create mode 100644 glutin/src/api/egl/display.rs delete mode 100644 glutin/src/api/egl/make_current_guard.rs create mode 100644 glutin/src/api/egl/surface.rs create mode 100644 glutin/src/api/glx/config.rs create mode 100644 glutin/src/api/glx/context.rs create mode 100644 glutin/src/api/glx/display.rs delete mode 100644 glutin/src/api/glx/make_current_guard.rs create mode 100644 glutin/src/api/glx/surface.rs delete mode 100644 glutin/src/api/ios/mod.rs delete mode 100644 glutin/src/api/osmesa/mod.rs delete mode 100644 glutin/src/api/wgl/make_current_guard.rs create mode 100644 glutin/src/config.rs create mode 100644 glutin/src/display.rs create mode 100644 glutin/src/error.rs create mode 100644 glutin/src/lib_loading.rs delete mode 100644 glutin/src/platform/android.rs delete mode 100644 glutin/src/platform/ios.rs delete mode 100644 glutin/src/platform/macos.rs delete mode 100644 glutin/src/platform/mod.rs delete mode 100644 glutin/src/platform/unix.rs delete mode 100644 glutin/src/platform/windows.rs delete mode 100644 glutin/src/platform_impl/ios/mod.rs create mode 100644 glutin/src/platform_impl/linux/mod.rs create mode 100644 glutin/src/platform_impl/linux/x11.rs delete mode 100644 glutin/src/platform_impl/macos/helpers.rs delete mode 100644 glutin/src/platform_impl/macos/mod.rs delete mode 100644 glutin/src/platform_impl/mod.rs delete mode 100644 glutin/src/platform_impl/unix/mod.rs delete mode 100644 glutin/src/platform_impl/unix/wayland.rs delete mode 100644 glutin/src/platform_impl/unix/x11.rs delete mode 100644 glutin/src/platform_impl/unix/x11/utils.rs create mode 100644 glutin/src/surface.rs delete mode 100644 glutin/src/windowed.rs delete mode 100644 glutin_examples/examples/buffer_age.rs delete mode 100644 glutin_examples/examples/damage.rs delete mode 100644 glutin_examples/examples/fullscreen.rs delete mode 100644 glutin_examples/examples/headless.rs delete mode 100644 glutin_examples/examples/multiwindow.rs delete mode 100644 glutin_examples/examples/raw_context.rs delete mode 100644 glutin_examples/examples/sharing.rs delete mode 100644 glutin_examples/examples/support/mod.rs delete mode 100644 glutin_examples/examples/transparent.rs diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index b4a8898921e..e13bc4764dd 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -3,63 +3,39 @@ name = "glutin" version = "0.28.0" authors = ["The glutin contributors", "Pierre Krieger "] description = "Cross-platform OpenGL context provider." -keywords = ["windowing", "opengl"] +keywords = ["windowing", "opengl", "egl", "glx", "wgl", "cgl"] license = "Apache-2.0" readme = "../README.md" repository = "https://github.com/rust-windowing/glutin" documentation = "https://docs.rs/glutin" -edition = "2018" - -[package.metadata.docs.rs] -features = ["serde"] +edition = "2021" [features] -serde = ["winit/serde"] -x11 = ["winit/x11", "glutin_glx_sys"] -wayland = ["winit/wayland", "winit/wayland-dlopen", "wayland-client", "wayland-egl"] -wayland-dlopen = ["winit/wayland-dlopen"] -default = ["x11", "wayland", "wayland-dlopen"] +default = ["egl", "glx", "x11", "wayland", "wgl"] +egl = ["glutin_egl_sys", "glutin_gles2_sys"] +glx = ["x11", "x11-dl", "glutin_glx_sys"] +wgl = [] +x11 = ["x11-dl"] +wayland = ["wayland-egl"] [dependencies] -lazy_static = "1.3" -winit = { version = "0.26", default-features = false } +libloading = "0.7.3" +once_cell = "1.12" +raw-window-handle = "0.5.0" +bitflags = "1.3.2" + +[target.'cfg(target_os = "windows")'.dependencies] +glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys", optional = true } +glutin_gles2_sys = { version = "0.1.5", path = "../glutin_gles2_sys", optional = true } [target.'cfg(target_os = "android")'.dependencies] glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys" } -libloading = "0.7" -parking_lot = "0.11" -raw-window-handle = "0.4" - -[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] -objc = "0.2.6" glutin_gles2_sys = { version = "0.1.5", path = "../glutin_gles2_sys" } -[target.'cfg(target_os = "macos")'.dependencies] -cgl = "0.3" -cocoa = "0.24" -core-foundation = "0.9" - -[target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3" -features = [ - "winnt", - "winuser", - "wingdi", - "libloaderapi", -] - -[target.'cfg(target_os = "windows")'.dependencies] -libloading = "0.7" -glutin_wgl_sys = { version = "0.1.5", path = "../glutin_wgl_sys" } -glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys" } -parking_lot = "0.11" - [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] -osmesa-sys = "0.1" -wayland-client = { version = "0.29", features = ["dlopen"], optional = true } -wayland-egl = { version = "0.29", optional = true } -libloading = "0.7" -glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys" } +wayland-egl = { version = "0.29.4", optional = true } +x11-dl = { version = "2.19.1", optional = true } glutin_glx_sys = { version = "0.1.7", path = "../glutin_glx_sys", optional = true } -parking_lot = "0.11" -log = "0.4" +glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys", optional = true } +glutin_gles2_sys = { version = "0.1.5", path = "../glutin_gles2_sys", optional = true } + diff --git a/glutin/src/api/dlloader.rs b/glutin/src/api/dlloader.rs deleted file mode 100644 index fa35b03412e..00000000000 --- a/glutin/src/api/dlloader.rs +++ /dev/null @@ -1,69 +0,0 @@ -#![cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", -))] - -use libloading::Library; - -#[cfg(target_os = "windows")] -use libloading::os::windows; - -#[cfg(target_os = "windows")] -use winapi::um::libloaderapi::*; - -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -#[derive(Clone)] -pub struct SymWrapper { - inner: T, - _lib: Arc, -} - -pub trait SymTrait { - fn load_with(lib: &Library) -> Self; -} - -impl SymWrapper { - pub fn new(lib_paths: Vec<&str>) -> Result { - for path in lib_paths { - // Avoid loading from PATH - #[cfg(target_os = "windows")] - let lib = unsafe { - windows::Library::load_with_flags(path, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) - .map(From::from) - }; - - #[cfg(not(target_os = "windows"))] - let lib = unsafe { Library::new(path) }; - - if lib.is_ok() { - return Ok(SymWrapper { - inner: T::load_with(lib.as_ref().unwrap()), - _lib: Arc::new(lib.unwrap()), - }); - } - } - - Err(()) - } -} - -impl Deref for SymWrapper { - type Target = T; - - fn deref(&self) -> &T { - &self.inner - } -} - -impl DerefMut for SymWrapper { - fn deref_mut(&mut self) -> &mut T { - &mut self.inner - } -} diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs new file mode 100644 index 00000000000..b77f0c60ac0 --- /dev/null +++ b/glutin/src/api/egl/config.rs @@ -0,0 +1,295 @@ +use std::mem; + +use raw_window_handle::RawDisplayHandle; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::{EGLConfig, EGLDisplay, EGLint}; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig, +}; + +#[cfg(feature = "x11")] +use crate::platform_impl::x11::X11VisualInfo; + +use super::display::Display; +use super::Egl; + +impl Display { + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + let mut config_attributes = Vec::::new(); + + // Add color buffer type. + match template.color_buffer_type { + ColorBufferType::Rgb { r_size, g_size, b_size } => { + // Type. + config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint); + config_attributes.push(egl::RGB_BUFFER as EGLint); + + // R. + config_attributes.push(egl::RED_SIZE as EGLint); + config_attributes.push(r_size as EGLint); + + // G. + config_attributes.push(egl::GREEN_SIZE as EGLint); + config_attributes.push(g_size as EGLint); + + // B. + config_attributes.push(egl::BLUE_SIZE as EGLint); + config_attributes.push(b_size as EGLint); + } + ColorBufferType::Luminance(luminance) => { + // Type. + config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint); + config_attributes.push(egl::LUMINANCE_BUFFER as EGLint); + + // L. + config_attributes.push(egl::LUMINANCE_SIZE as EGLint); + config_attributes.push(luminance as EGLint); + } + }; + + // Add alpha. + config_attributes.push(egl::ALPHA_SIZE as EGLint); + config_attributes.push(template.alpha_size as EGLint); + + // Add depth. + config_attributes.push(egl::DEPTH_SIZE as EGLint); + config_attributes.push(template.depth_size as EGLint); + + // Add stencil. + config_attributes.push(egl::STENCIL_SIZE as EGLint); + config_attributes.push(template.stencil_size as EGLint); + + // Add surface type. + config_attributes.push(egl::SURFACE_TYPE as EGLint); + let mut surface_type = 0; + if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { + surface_type |= egl::WINDOW_BIT; + } + if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { + surface_type |= egl::PBUFFER_BIT; + } + if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { + surface_type |= egl::PIXMAP_BIT; + } + config_attributes.push(surface_type as EGLint); + + // Add minimum swap interval. + if let Some(min_swap_interval) = template.min_swap_interval { + config_attributes.push(egl::MIN_SWAP_INTERVAL as EGLint); + config_attributes.push(min_swap_interval as EGLint) + } + + // Add maximum swap interval. + if let Some(max_swap_interval) = template.max_swap_interval { + config_attributes.push(egl::MAX_SWAP_INTERVAL as EGLint); + config_attributes.push(max_swap_interval as EGLint) + } + + // Add samples. + config_attributes.push(egl::SAMPLE_BUFFERS as EGLint); + config_attributes.push(template.sample_buffers as EGLint); + + let mut api = 0; + if template.api.contains(Api::GLES1) { + api |= egl::OPENGL_ES_API; + } + if template.api.contains(Api::GLES2) { + api |= egl::OPENGL_ES2_BIT; + } + if template.api.contains(Api::GLES3) { + api |= egl::OPENGL_ES3_BIT; + } + if template.api.contains(Api::OPENGL) { + api |= egl::OPENGL_BIT; + } + config_attributes.push(egl::RENDERABLE_TYPE as EGLint); + config_attributes.push(api as EGLint); + + // Add maximum height of pbuffer. + if let Some(pbuffer_width) = template.max_pbuffer_width { + config_attributes.push(egl::MAX_PBUFFER_WIDTH as EGLint); + config_attributes.push(pbuffer_width as EGLint); + } + + // Add maximum width of pbuffer. + if let Some(pbuffer_height) = template.max_pbuffer_height { + config_attributes.push(egl::MAX_PBUFFER_HEIGHT as EGLint); + config_attributes.push(pbuffer_height as EGLint); + } + + // Push `egl::NONE` to terminate the list. + config_attributes.push(egl::NONE as EGLint); + + let mut configs_number = self.configs_number() as EGLint; + let mut found_configs: Vec = + unsafe { vec![mem::zeroed(); configs_number as usize] }; + + unsafe { + let result = self.egl.ChooseConfig( + self.display, + config_attributes.as_ptr(), + found_configs.as_mut_ptr(), + configs_number as EGLint, + &mut configs_number, + ); + + if result == egl::FALSE { + return None; + } + + found_configs.set_len(configs_number as usize); + } + + let supports_srgb = self.client_extensions.contains("EGL_KHR_gl_colorspace"); + + let configs = found_configs + .into_iter() + .map(move |raw| Config { + egl: self.egl, + display: self.display, + config: raw, + supports_srgb, + _raw_display: self.raw_display, + }) + .filter(move |_config| { + #[cfg(feature = "x11")] + if template.transparency { + if let RawDisplayHandle::Xlib(display_handle) = self.raw_display { + let xid = _config.native_visual(); + let xid = unsafe { + X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) + }; + return xid.map_or(false, |xid| xid.supports_transparency()); + } + } + + true + }); + + Some(Box::new(configs)) + } + + fn configs_number(&self) -> usize { + unsafe { + let mut num_configs = 0; + self.egl.GetConfigs(self.display, std::ptr::null_mut(), 0, &mut num_configs); + num_configs as usize + } + } +} + +pub struct Config { + egl: &'static Egl, + display: EGLDisplay, + pub(crate) config: EGLConfig, + supports_srgb: bool, + _raw_display: RawDisplayHandle, +} + +impl Config { + pub fn color_buffer_type(&self) -> ColorBufferType { + match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ { + egl::LUMINANCE_BUFFER => { + let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint); + ColorBufferType::Luminance(luma as u8) + } + egl::RGB_BUFFER => { + let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8; + let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8; + let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8; + ColorBufferType::Rgb { r_size, g_size, b_size } + } + _ => unreachable!(), + } + } + + #[cfg(feature = "x11")] + pub fn x11_visual(&self) -> Option { + match self._raw_display { + RawDisplayHandle::Xlib(display_handle) => unsafe { + let xid = self.native_visual(); + X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) + }, + _ => None, + } + } + + pub fn native_visual(&self) -> u32 { + self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 + } + + pub fn alpha_size(&self) -> u8 { + self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 + } + + pub fn srgb_capable(&self) -> bool { + self.supports_srgb + } + + pub fn depth_size(&self) -> u8 { + self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 + } + + pub fn stencil_size(&self) -> u8 { + self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 + } + + pub fn sample_buffers(&self) -> u8 { + self.raw_attribute(egl::SAMPLE_BUFFERS as EGLint) as u8 + } + + pub fn config_surface_types(&self) -> ConfigSurfaceTypes { + let mut ty = ConfigSurfaceTypes::empty(); + + let raw_ty = self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32; + if raw_ty & egl::WINDOW_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::WINDOW); + } + if raw_ty & egl::PBUFFER_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PBUFFER); + } + if raw_ty & egl::PIXMAP_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PIXMAP); + } + + ty + } + + pub fn api(&self) -> Api { + let mut api = Api::empty(); + let raw_api = self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32; + if raw_api & egl::OPENGL_BIT as u32 != 0 { + api.insert(Api::OPENGL); + } + if raw_api & egl::OPENGL_ES_BIT as u32 != 0 { + api.insert(Api::GLES1); + } + if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 { + api.insert(Api::GLES2); + } + if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 { + api.insert(Api::GLES3); + } + + api + } + + fn raw_attribute(&self, attr: EGLint) -> EGLint { + unsafe { + let mut val = 0; + self.egl.GetConfigAttrib(self.display, self.config, attr, &mut val); + val as EGLint + } + } +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + RawConfig::Egl(self.config) + } +} diff --git a/glutin/src/api/egl/context.rs b/glutin/src/api/egl/context.rs new file mode 100644 index 00000000000..f2061bcbd7e --- /dev/null +++ b/glutin/src/api/egl/context.rs @@ -0,0 +1,186 @@ +use std::cell::Cell; +use std::ffi::{self, CStr}; +use std::marker::PhantomData; +use std::rc::Rc; + +use glutin_egl_sys::egl::types::{EGLDisplay, EGLint}; +use glutin_egl_sys::{egl, EGLContext}; + +use crate::config::Api; +use crate::context::{AsRawContext, ContextAttributes, GlProfile, RawContext}; +use crate::error::{ErrorKind, Result}; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; +use super::Egl; + +impl Display { + pub fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let mut attrs = Vec::::new(); + + // Get the api supported by the config. + let api = config.api(); + + let supports_opengl = !(self.version.major <= 1 && self.version.minor <= 3); + let supports_gles = !(self.version.major <= 1 && self.version.minor <= 1); + + let api = match context_attributes.profile { + Some(GlProfile::Core) if supports_opengl => egl::OPENGL_API, + Some(GlProfile::Compatibility) if supports_gles => egl::OPENGL_ES_API, + None if api.contains(Api::OPENGL) && supports_opengl => egl::OPENGL_API, + None if supports_gles => egl::OPENGL_ES_API, + _ => return Err(ErrorKind::NotSupported.into()), + }; + + if context_attributes.debug && !(self.version.major <= 1 && self.version.minor <= 4) { + attrs.push(egl::CONTEXT_OPENGL_DEBUG as EGLint); + attrs.push(egl::TRUE as EGLint); + } + + attrs.push(egl::NONE as EGLint); + + let shared_context = + if let Some(shared_context) = context_attributes.shared_context.as_ref() { + match shared_context { + RawContext::Egl(shared_context) => *shared_context, + _ => unreachable!(), + } + } else { + egl::NO_CONTEXT + }; + + // Bind the api. + unsafe { + if self.egl.BindAPI(api) == egl::FALSE { + return Err(super::check_error().err().unwrap()); + } + } + + unsafe { + let context = + self.egl.CreateContext(self.display, config.config, shared_context, attrs.as_ptr()); + + if context == egl::NO_CONTEXT { + return Err(super::check_error().err().unwrap()); + } + + let inner = ContextInner { egl: self.egl, display: self.display, context }; + Ok(NotCurrentContext::new(inner)) + } + } +} + +struct ContextInner { + egl: &'static Egl, + display: EGLDisplay, + context: EGLContext, +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread, a. + _nosendsync: PhantomData>, +} + +impl PossiblyCurrentContext { + pub fn make_not_current(self) -> NotCurrentContext { + self.inner.make_not_current(); + NotCurrentContext::new(self.inner) + } + + pub fn make_current(&self, surface: &Surface) { + self.inner.make_current_draw_read(surface, surface) + } + + pub fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + self.inner.make_current_draw_read(surface_draw, surface_read) + } + + pub fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + pub fn is_current(&self) -> bool { + unsafe { self.inner.egl.GetCurrentContext() == self.inner.context } + } + + pub fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { self.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ } + } +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Egl(self.inner.context) + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Egl(self.inner.context) + } +} + +pub struct NotCurrentContext { + inner: ContextInner, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner, _nosync: PhantomData } + } + + pub fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } + + pub fn make_current(self, surface: &Surface) -> PossiblyCurrentContext { + self.inner.make_current_draw_read(surface, surface); + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } + + pub fn make_current_draw_read( + self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> PossiblyCurrentContext { + self.inner.make_current_draw_read(surface_draw, surface_read); + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + unsafe { + let draw = surface_draw.surface; + let read = surface_read.surface; + self.egl.MakeCurrent(self.display, draw, read, self.context); + } + } + + fn make_not_current(&self) { + unsafe { + self.egl.MakeCurrent(self.display, egl::NO_SURFACE, egl::NO_SURFACE, egl::NO_CONTEXT); + } + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs new file mode 100644 index 00000000000..3a61f3e10bf --- /dev/null +++ b/glutin/src/api/egl/display.rs @@ -0,0 +1,220 @@ +use std::collections::HashSet; +use std::ffi::CStr; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; + +use once_cell::sync::OnceCell; + +use raw_window_handle::RawDisplayHandle; + +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; + +use super::{Egl, EGL}; + +/// Extensions that don't require any display. +static NO_DISPLAY_EXTENSIONS: OnceCell> = OnceCell::new(); + +pub struct EglDisplay(EGLDisplay); + +// Representation of EGL version. +#[derive(Default, Clone, Copy)] +pub(crate) struct EglVersion { + pub(crate) major: i32, + pub(crate) minor: i32, +} + +/// A Display to wrap EGLDisplay and its supported extensions. +pub struct Display { + /// Pointer to the EGL handler to simplify API calls. + pub(crate) egl: &'static Egl, + + /// Pointer to the egl display. + pub(crate) display: EGLDisplay, + + /// The version of the egl library. + pub(crate) version: EglVersion, + + /// Client EGL extensions. + pub(crate) client_extensions: HashSet<&'static str>, + + /// The raw display used to create EGL display. + pub(crate) raw_display: RawDisplayHandle, +} + +impl Display { + /// Create EGL display. + /// + /// # Safety + /// + /// `RawDisplay` must point to a valid system display. + pub unsafe fn from_raw(raw_display: RawDisplayHandle) -> Result { + let egl = match EGL.as_ref() { + Some(egl) => egl, + None => return Err(ErrorKind::NotFound.into()), + }; + + NO_DISPLAY_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); + + // Create a EGL display by chaining all display creation functions aborting on + // `EGL_BAD_ATTRIBUTE`. + let display = Self::get_platform_display(egl, raw_display) + .or_else(|err| { + if err.error_kind() == ErrorKind::BadAttribute { + Err(err) + } else { + Self::get_platform_display_ext(egl, raw_display) + } + }) + .or_else(|err| { + if err.error_kind() == ErrorKind::BadAttribute { + Err(err) + } else { + Self::get_display(egl, raw_display) + } + })?; + + let mut version = EglVersion::default(); + if egl.Initialize(display, &mut version.major, &mut version.minor) == egl::FALSE { + return Err(super::check_error().err().unwrap()); + } + + // Load extensions. + let client_extensions = get_extensions(egl, display); + + Ok(Self { egl, display, raw_display, version, client_extensions }) + } + + fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result { + if !egl.GetPlatformDisplay.is_loaded() { + return Err(ErrorKind::NotSupported.into()); + } + + let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + let mut attrs = Vec::::new(); + let (platform, mut display) = match display { + #[cfg(feature = "wayland")] + RawDisplayHandle::Wayland(handle) + if extensions.contains("EGL_KHR_platform_wayland") => + { + (egl::PLATFORM_WAYLAND_KHR, handle.display) + } + #[cfg(feature = "x11")] + RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => { + attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib); + attrs.push(handle.screen as EGLAttrib); + (egl::PLATFORM_X11_KHR, handle.display) + } + RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => { + (egl::PLATFORM_GBM_KHR, handle.gbm_device) + } + RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => { + (egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _) + } + _ => return Err(ErrorKind::NotSupported.into()), + }; + + // Be explicit here. + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + let display = + unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) }; + + Self::check_display_error(display) + } + + fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result { + if !egl.GetPlatformDisplayEXT.is_loaded() { + return Err(ErrorKind::NotSupported.into()); + } + + let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + let mut attrs = Vec::::new(); + let (platform, mut display) = match display { + #[cfg(feature = "wayland")] + RawDisplayHandle::Wayland(handle) + if extensions.contains("EGL_EXT_platform_wayland") => + { + (egl::PLATFORM_WAYLAND_EXT, handle.display) + } + #[cfg(feature = "x11")] + RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11") => { + attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint); + attrs.push(handle.screen as EGLint); + (egl::PLATFORM_X11_EXT, handle.display) + } + RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm") => { + (egl::PLATFORM_GBM_MESA, handle.gbm_device) + } + _ => return Err(ErrorKind::NotSupported.into()), + }; + + // Be explicit here. + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLint); + + let display = + unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) }; + + Self::check_display_error(display) + } + + fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result { + let mut display = match display { + RawDisplayHandle::Gbm(handle) => handle.gbm_device, + #[cfg(feature = "x11")] + RawDisplayHandle::Xlib(handle) => handle.display, + RawDisplayHandle::Android(_) => egl::DEFAULT_DISPLAY as *mut _, + _ => return Err(ErrorKind::NotSupported.into()), + }; + + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + let display = unsafe { egl.GetDisplay(display) }; + Self::check_display_error(display) + } + + fn check_display_error(display: EGLDisplay) -> Result { + if display == egl::NO_DISPLAY { + Err(super::check_error().err().unwrap()) + } else { + Ok(display) + } + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Egl(self.display) + } +} + +/// Collect egl extensions for the given `display`. +fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { + unsafe { + let extensions = egl.QueryString(display, egl::EXTENSIONS as i32); + if extensions.is_null() { + return HashSet::new(); + } + + if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { + extensions.split(' ').collect::>() + } else { + HashSet::new() + } + } +} diff --git a/glutin/src/api/egl/make_current_guard.rs b/glutin/src/api/egl/make_current_guard.rs deleted file mode 100644 index 06911900b8e..00000000000 --- a/glutin/src/api/egl/make_current_guard.rs +++ /dev/null @@ -1,98 +0,0 @@ -use glutin_egl_sys as ffi; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct MakeCurrentGuard { - display: ffi::egl::types::EGLDisplay, - old_display: ffi::egl::types::EGLDisplay, - possibly_invalid: Option, -} - -#[derive(Debug, PartialEq)] -struct MakeCurrentGuardInner { - old_draw_surface: ffi::egl::types::EGLSurface, - old_read_surface: ffi::egl::types::EGLSurface, - old_context: ffi::egl::types::EGLContext, -} - -impl MakeCurrentGuard { - pub fn new( - display: ffi::egl::types::EGLDisplay, - draw_surface: ffi::egl::types::EGLSurface, - read_surface: ffi::egl::types::EGLSurface, - context: ffi::egl::types::EGLContext, - ) -> Result { - unsafe { - let egl = super::EGL.as_ref().unwrap(); - - let mut ret = MakeCurrentGuard { - display, - old_display: egl.GetCurrentDisplay(), - possibly_invalid: Some(MakeCurrentGuardInner { - old_draw_surface: egl.GetCurrentSurface(ffi::egl::DRAW as i32), - old_read_surface: egl.GetCurrentSurface(ffi::egl::READ as i32), - old_context: egl.GetCurrentContext(), - }), - }; - - if ret.old_display == ffi::egl::NO_DISPLAY { - ret.invalidate(); - } - - let res = egl.MakeCurrent(display, draw_surface, read_surface, context); - - if res == 0 { - let err = egl.GetError(); - Err(format!("`eglMakeCurrent` failed: 0x{:x}", err)) - } else { - Ok(ret) - } - } - } - - pub fn if_any_same_then_invalidate( - &mut self, - draw_surface: ffi::egl::types::EGLSurface, - read_surface: ffi::egl::types::EGLSurface, - context: ffi::egl::types::EGLContext, - ) { - if self.possibly_invalid.is_some() { - let pi = self.possibly_invalid.as_ref().unwrap(); - if pi.old_draw_surface == draw_surface && draw_surface != ffi::egl::NO_SURFACE - || pi.old_read_surface == read_surface && read_surface != ffi::egl::NO_SURFACE - || pi.old_context == context - { - self.invalidate(); - } - } - } - - pub fn invalidate(&mut self) { - self.possibly_invalid.take(); - } -} - -impl Drop for MakeCurrentGuard { - fn drop(&mut self) { - let egl = super::EGL.as_ref().unwrap(); - let (draw_surface, read_surface, context) = match self.possibly_invalid.take() { - Some(inner) => (inner.old_draw_surface, inner.old_read_surface, inner.old_context), - None => (ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, ffi::egl::NO_CONTEXT), - }; - - let display = match self.old_display { - ffi::egl::NO_DISPLAY => self.display, - old_display => old_display, - }; - - unsafe { - let res = egl.MakeCurrent(display, draw_surface, read_surface, context); - - if res == 0 { - let err = egl.GetError(); - panic!("`eglMakeCurrent` failed: 0x{:x}", err) - } - } - } -} diff --git a/glutin/src/api/egl/mod.rs b/glutin/src/api/egl/mod.rs index fad3fb89124..f6336e6426d 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -1,1320 +1,111 @@ -#![cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#![allow(unused_variables)] - -mod egl { - use super::ffi; - use crate::api::dlloader::{SymTrait, SymWrapper}; - use libloading; - use std::sync::{Arc, Mutex}; - - #[cfg(unix)] - use libloading::os::unix as libloading_os; - #[cfg(windows)] - use libloading::os::windows as libloading_os; - - #[derive(Clone)] - pub struct Egl(pub SymWrapper); - - /// Because `*const raw::c_void` doesn't implement `Sync`. - unsafe impl Sync for Egl {} - - type EglGetProcAddressType = libloading_os::Symbol< - unsafe extern "C" fn(*const std::os::raw::c_void) -> *const std::os::raw::c_void, - >; - - lazy_static! { - static ref EGL_GET_PROC_ADDRESS: Arc>> = - Arc::new(Mutex::new(None)); - } - - impl SymTrait for ffi::egl::Egl { - fn load_with(lib: &libloading::Library) -> Self { - let f = move |s: &'static str| -> *const std::os::raw::c_void { - // Check if the symbol is available in the library directly. If - // it is, just return it. - match unsafe { - lib.get(std::ffi::CString::new(s.as_bytes()).unwrap().as_bytes_with_nul()) - } { - Ok(sym) => return *sym, - Err(_) => (), - }; - - let mut egl_get_proc_address = (*EGL_GET_PROC_ADDRESS).lock().unwrap(); - if egl_get_proc_address.is_none() { - unsafe { - let sym: libloading::Symbol< - unsafe extern "C" fn( - *const std::os::raw::c_void, - ) - -> *const std::os::raw::c_void, - > = lib.get(b"eglGetProcAddress").unwrap(); - *egl_get_proc_address = Some(sym.into_raw()); - } - } - - // The symbol was not available in the library, so ask - // eglGetProcAddress for it. Note that eglGetProcAddress was - // only able to look up extension functions prior to EGL 1.5, - // hence this two-part dance. - unsafe { - (egl_get_proc_address.as_ref().unwrap())( - std::ffi::CString::new(s.as_bytes()).unwrap().as_bytes_with_nul().as_ptr() - as *const std::os::raw::c_void, - ) - } - }; - - Self::load_with(f) - } - } - - impl Egl { - pub fn new() -> Result { - #[cfg(target_os = "windows")] - let paths = vec!["libEGL.dll", "atioglxx.dll"]; - - #[cfg(not(target_os = "windows"))] - let paths = vec!["libEGL.so.1", "libEGL.so"]; - - SymWrapper::new(paths).map(|i| Egl(i)) - } - } -} - -mod make_current_guard; - -pub use self::egl::Egl; -use self::make_current_guard::MakeCurrentGuard; -#[cfg(not(target_os = "windows"))] -use crate::Rect; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; - -use glutin_egl_sys as ffi; -use parking_lot::Mutex; -#[cfg(any( - target_os = "android", - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -use winit::dpi; - -use std::ffi::{CStr, CString}; +use std::ffi::{self, CString}; use std::ops::{Deref, DerefMut}; -use std::os::raw; -impl Deref for Egl { - type Target = ffi::egl::Egl; +use glutin_egl_sys::egl; - fn deref(&self) -> &Self::Target { - &self.0 - } -} +use libloading::Library; +use once_cell::sync::{Lazy, OnceCell}; -impl DerefMut for Egl { - fn deref_mut(&mut self) -> &mut ffi::egl::Egl { - &mut self.0 - } -} +#[cfg(unix)] +use libloading::os::unix as libloading_os; +#[cfg(windows)] +use libloading::os::windows as libloading_os; -lazy_static! { - pub static ref EGL: Option = Egl::new().ok(); -} +use crate::error::{Error, ErrorKind, Result}; +use crate::lib_loading::{SymLoading, SymWrapper}; -/// Specifies the type of display passed as `native_display`. -#[derive(Debug)] -#[allow(dead_code)] -pub enum NativeDisplay { - /// `None` means `EGL_DEFAULT_DISPLAY`. - X11(Option), - /// `None` means `EGL_DEFAULT_DISPLAY`. - Gbm(Option), - /// `None` means `EGL_DEFAULT_DISPLAY`. - Wayland(Option), - /// `EGL_DEFAULT_DISPLAY` is mandatory for Android. - Android, - // TODO: should be `EGLDeviceEXT` - Device(ffi::EGLNativeDisplayType), - /// Don't specify any display type. Useful on windows. `None` means - /// `EGL_DEFAULT_DISPLAY`. - Other(Option), -} +pub struct Egl(pub SymWrapper); -#[derive(Debug)] -pub struct Context { - display: ffi::egl::types::EGLDisplay, - context: ffi::egl::types::EGLContext, - surface: Option>, - api: Api, - pixel_format: PixelFormat, -} +pub mod config; +pub mod context; +pub mod display; +pub mod surface; -fn get_egl_version( - display: ffi::egl::types::EGLDisplay, -) -> Result<(ffi::egl::types::EGLint, ffi::egl::types::EGLint), CreationError> { - unsafe { - let egl = EGL.as_ref().unwrap(); - let mut major: ffi::egl::types::EGLint = std::mem::zeroed(); - let mut minor: ffi::egl::types::EGLint = std::mem::zeroed(); +unsafe impl Sync for Egl {} - if egl.Initialize(display, &mut major, &mut minor) == 0 { - return Err(CreationError::OsError("eglInitialize failed".to_string())); - } +pub static EGL: Lazy> = Lazy::new(|| { + #[cfg(windows)] + let paths = ["libEGL.dll", "atioglxx.dll"]; - Ok((major, minor)) - } -} + #[cfg(not(windows))] + let paths = ["libEGL.so.1", "libEGL.so"]; -unsafe fn bind_and_get_api<'a>( - opengl: &'a GlAttributes<&'a Context>, - egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), -) -> Result<(Option<(u8, u8)>, Api), CreationError> { - let egl = EGL.as_ref().unwrap(); - match opengl.version { - GlRequest::Latest => { - if egl_version >= (1, 4) { - if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { - Ok((None, Api::OpenGl)) - } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { - Ok((None, Api::OpenGlEs)) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else { - Ok((None, Api::OpenGlEs)) - } - } - GlRequest::Specific(Api::OpenGlEs, version) => { - if egl_version >= (1, 2) { - if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); - } - } - Ok((Some(version), Api::OpenGlEs)) - } - GlRequest::Specific(Api::OpenGl, version) => { - if egl_version < (1, 4) { - return Err(CreationError::OpenGlVersionNotSupported); - } - if egl.BindAPI(ffi::egl::OPENGL_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); - } - Ok((Some(version), Api::OpenGl)) - } - GlRequest::Specific(_, _) => Err(CreationError::OpenGlVersionNotSupported), - GlRequest::GlThenGles { opengles_version, opengl_version } => { - if egl_version >= (1, 4) { - if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { - Ok((Some(opengl_version), Api::OpenGl)) - } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { - Ok((Some(opengles_version), Api::OpenGlEs)) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else { - Ok((Some(opengles_version), Api::OpenGlEs)) - } - } + match SymWrapper::new(&paths).map(Egl) { + Ok(egl) => Some(egl), + Err(_) => None, } -} +}); -fn get_native_display(native_display: &NativeDisplay) -> *const raw::c_void { - let egl = EGL.as_ref().unwrap(); - // the first step is to query the list of extensions without any display, if - // supported - let dp_extensions = unsafe { - let p = 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(|_| format!("")); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - - let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some(); - - match *native_display { - // Note: Some EGL implementations are missing the - // `eglGetPlatformDisplay(EXT)` symbol despite reporting - // `EGL_EXT_platform_base`. I'm pretty sure this is a bug. - // Therefore we detect whether the symbol is loaded in addition to - // checking for extensions. - NativeDisplay::X11(display) - if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - // TODO: `PLATFORM_X11_SCREEN_KHR` - unsafe { - egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::X11(display) - if has_dp_extension("EGL_EXT_platform_x11") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - // TODO: `PLATFORM_X11_SCREEN_EXT` - unsafe { - egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Gbm(display) - if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Gbm(display) - if has_dp_extension("EGL_MESA_platform_gbm") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Wayland(display) - if has_dp_extension("EGL_KHR_platform_wayland") - && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_WAYLAND_KHR, - d as *mut _, - std::ptr::null(), - ) - } - } +type EglGetProcAddress = unsafe extern "C" fn(*const ffi::c_void) -> *const ffi::c_void; +static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); - NativeDisplay::Wayland(display) - if has_dp_extension("EGL_EXT_platform_wayland") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); +impl SymLoading for egl::Egl { + fn load_with(lib: &Library) -> Self { + let loader = move |sym_name: &'static str| -> *const ffi::c_void { + let sym_name = CString::new(sym_name.as_bytes()).unwrap(); unsafe { - egl.GetPlatformDisplayEXT( - ffi::egl::PLATFORM_WAYLAND_EXT, - d as *mut _, - std::ptr::null(), - ) - } - } - - NativeDisplay::Android - if has_dp_extension("EGL_KHR_platform_android") - && egl.GetPlatformDisplay.is_loaded() => - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_ANDROID_KHR, - ffi::egl::DEFAULT_DISPLAY as *mut _, - std::ptr::null(), - ) - }, - - NativeDisplay::Device(display) - if has_dp_extension("EGL_EXT_platform_device") - && egl.GetPlatformDisplay.is_loaded() => - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_DEVICE_EXT, - display as *mut _, - std::ptr::null(), - ) - }, - - NativeDisplay::X11(Some(display)) - | NativeDisplay::Gbm(Some(display)) - | NativeDisplay::Wayland(Some(display)) - | NativeDisplay::Device(display) - | NativeDisplay::Other(Some(display)) => unsafe { egl.GetDisplay(display as *mut _) }, - - NativeDisplay::X11(None) - | NativeDisplay::Gbm(None) - | NativeDisplay::Wayland(None) - | NativeDisplay::Android - | NativeDisplay::Other(None) => unsafe { - egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) - }, - } -} - -#[allow(dead_code)] // Not all platforms use all -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum SurfaceType { - PBuffer, - Window, - Surfaceless, -} - -impl Context { - /// Start building an EGL context. - /// - /// This function initializes some things and chooses the pixel format. - /// - /// To finish the process, you must call `.finish(window)` on the - /// `ContextPrototype`. - pub fn new<'a, F>( - pf_reqs: &PixelFormatRequirements, - opengl: &'a GlAttributes<&'a Context>, - native_display: NativeDisplay, - surface_type: SurfaceType, - config_selector: F, - ) -> Result, CreationError> - where - F: FnMut( - Vec, - ffi::egl::types::EGLDisplay, - ) -> Result, - { - let egl = EGL.as_ref().unwrap(); - // calling `eglGetDisplay` or equivalent - let display = get_native_display(&native_display); - - if display.is_null() { - return Err(CreationError::OsError("Could not create EGL display object".to_string())); - } - - let egl_version = get_egl_version(display)?; - - // 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(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) }; - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!("")); - list.split(' ').map(|e| e.to_string()).collect::>() - } else { - vec![] - }; - - // binding the right API and choosing the version - let (version, api) = unsafe { bind_and_get_api(&opengl, egl_version)? }; - - let (config_id, pixel_format) = unsafe { - choose_fbconfig( - display, - &egl_version, - api, - version, - pf_reqs, - surface_type, - opengl, - config_selector, - )? - }; - - Ok(ContextPrototype { - opengl, - display, - egl_version, - extensions, - api, - version, - config_id, - pixel_format, - }) - } - - unsafe fn check_make_current(&self, ret: Option) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - if ret == Some(0) { - match egl.GetError() as u32 { - ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost), - err => { - panic!("make_current: eglMakeCurrent failed (eglGetError returned 0x{:x})", err) + if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { + return *sym; } } - } else { - Ok(()) - } - } - - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE); - let ret = egl.MakeCurrent(self.display, surface, surface, self.context); - - self.check_make_current(Some(ret)) - } - - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - - let surface_eq = if let Some(surface) = self.surface.as_ref().map(|s| *s.lock()) { - egl.GetCurrentSurface(ffi::egl::DRAW as i32) == surface - || egl.GetCurrentSurface(ffi::egl::READ as i32) == surface - } else { - false - }; - - if surface_eq || egl.GetCurrentContext() == self.context { - let ret = egl.MakeCurrent( - self.display, - ffi::egl::NO_SURFACE, - ffi::egl::NO_SURFACE, - ffi::egl::NO_CONTEXT, - ); - - self.check_make_current(Some(ret)) - } else { - self.check_make_current(None) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - let egl = EGL.as_ref().unwrap(); - unsafe { egl.GetCurrentContext() == self.context } - } - #[inline] - pub fn get_api(&self) -> Api { - self.api - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::egl::types::EGLContext { - self.context - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> ffi::egl::types::EGLDisplay { - self.display - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let egl = EGL.as_ref().unwrap(); - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - unsafe { egl.GetProcAddress(addr) as *const _ } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().unwrap().lock(); - if *surface == ffi::egl::NO_SURFACE { - return Err(ContextError::ContextLost); - } - - let ret = unsafe { egl.SwapBuffers(self.display, *surface) }; + let egl_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| unsafe { + let sym: libloading::Symbol = + lib.get(b"eglGetProcAddress\0").unwrap(); + sym.into_raw() + }); - if ret == 0 { - match unsafe { egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost), - err => { - panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err) - } + // The symbol was not available in the library, so ask eglGetProcAddress for it. Note + // that eglGetProcAddress was only able to look up extension functions prior to EGL + // 1.5, hence this two-part dance. + unsafe { + (egl_proc_address)(sym_name.as_bytes_with_nul().as_ptr() as *const ffi::c_void) } - } else { - Ok(()) - } - } - - #[inline] - #[cfg(not(target_os = "windows"))] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - - if !egl.SwapBuffersWithDamageKHR.is_loaded() { - return Err(ContextError::FunctionUnavailable); - } - - let surface = self.surface.as_ref().unwrap().lock(); - if *surface == ffi::egl::NO_SURFACE { - return Err(ContextError::ContextLost); - } - - let mut ffirects: Vec = Vec::with_capacity(rects.len() * 4); - - for rect in rects { - ffirects.push(rect.x as ffi::egl::types::EGLint); - ffirects.push(rect.y as ffi::egl::types::EGLint); - ffirects.push(rect.width as ffi::egl::types::EGLint); - ffirects.push(rect.height as ffi::egl::types::EGLint); - } - - let ret = unsafe { - egl.SwapBuffersWithDamageKHR( - self.display, - *surface, - ffirects.as_mut_ptr(), - rects.len() as ffi::egl::types::EGLint, - ) }; - if ret == ffi::egl::FALSE { - match unsafe { egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost), - err => { - panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err) - } - } - } else { - Ok(()) - } - } - - #[inline] - #[cfg(not(target_os = "windows"))] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - let egl = EGL.as_ref().unwrap(); - egl.SwapBuffersWithDamageKHR.is_loaded() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().unwrap().lock(); - - let mut buffer_age = 0; - let result = unsafe { - egl.QuerySurface( - self.display, - *surface as *const _, - ffi::egl::BUFFER_AGE_EXT as i32, - &mut buffer_age, - ) - }; - - if result == ffi::egl::FALSE { - 0 - } else { - buffer_age as u32 - } + Self::load_with(loader) } } -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Drop for Context { - fn drop(&mut self) { - unsafe { - // https://stackoverflow.com/questions/54402688/recreate-eglcreatewindowsurface-with-same-native-window - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE); - // Ok, so we got to call `glFinish` before destroying the context - // to ensure it actually gets destroyed. This requires making the - // this context current. - let mut guard = MakeCurrentGuard::new(self.display, surface, surface, self.context) - .map_err(|err| ContextError::OsError(err)) - .unwrap(); - - guard.if_any_same_then_invalidate(surface, surface, self.context); - - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(gl_finish_fn != std::ptr::null()); - let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn); - gl_finish_fn(); - - egl.DestroyContext(self.display, self.context); - self.context = ffi::egl::NO_CONTEXT; - egl.DestroySurface(self.display, surface); - if let Some(ref surface) = self.surface { - let mut surface = surface.lock(); - *surface = ffi::egl::NO_SURFACE; - } +impl Deref for Egl { + type Target = egl::Egl; - // In a reasonable world, we could uncomment the line bellow. - // - // This is no such world. Lets talk about something. - // - // You see, every call to `get_native_display` returns the exact - // same display, just look at the docs: - // - // "Multiple calls made to eglGetDisplay with the same display_id - // will return the same EGLDisplay handle." - // - // My EGL implementation does not do any ref counting, nor do the - // EGL docs mention ref counting anywhere. In fact, the docs state - // that there will be *no effect*, which, in a way, implies no ref - // counting: - // - // "Initializing an already initialized EGL display connection has - // no effect besides returning the version numbers." - // - // So, if we terminate the display, other people who are using it - // won't be so happy. - // - // Well, how did I stumble on this issue you might ask... - // - // In this case, the "other people" was us, for it appears my EGL - // implementation does not follow the docs, or maybe I'm misreading - // them. You see, according to the egl docs: - // - // "To release the current context without assigning a new one, set - // context to EGL_NO_CONTEXT and set draw and read to - // EGL_NO_SURFACE. [...] ******This is the only case where an - // uninitialized display may be passed to eglMakeCurrent.******" - // (Emphasis mine). - // - // Well, my computer returns EGL_BAD_DISPLAY if the display passed - // to eglMakeCurrent is uninitialized, which allowed to me to spot - // this issue. - // - // I would have assumed that if EGL was going to provide us with - // the same EGLDisplay that they'd at least do - // some ref counting, but they don't. - // - // FIXME: Technically we are leaking resources, not much we can do. - // Yeah, we could have a global static that does ref counting - // ourselves, but what if some other library is using the display. - // - // On unix operating systems, we could preload a little lib that - // does ref counting on that level, but: - // A) What about other platforms? - // B) Do you *really* want all glutin programs to preload a - // library? - // C) Who the hell is going to maintain that? - // - // egl.Terminate(self.display); - } + fn deref(&self) -> &Self::Target { + &self.0 } } -#[derive(Debug)] -pub struct ContextPrototype<'a> { - opengl: &'a GlAttributes<&'a Context>, - display: ffi::egl::types::EGLDisplay, - egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), - extensions: Vec, - api: Api, - version: Option<(u8, u8)>, - config_id: ffi::egl::types::EGLConfig, - pixel_format: PixelFormat, -} - -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#[cfg(feature = "x11")] -pub fn get_native_visual_id( - display: ffi::egl::types::EGLDisplay, - config_id: ffi::egl::types::EGLConfig, -) -> ffi::egl::types::EGLint { - let egl = EGL.as_ref().unwrap(); - let mut value = unsafe { std::mem::zeroed() }; - let ret = unsafe { - egl.GetConfigAttrib( - display, - config_id, - ffi::egl::NATIVE_VISUAL_ID as ffi::egl::types::EGLint, - &mut value, - ) - }; - if ret == 0 { - panic!("get_native_visual_id: eglGetConfigAttrib failed with 0x{:x}", unsafe { - egl.GetError() - }) - }; - value -} - -impl<'a> ContextPrototype<'a> { - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - #[cfg(feature = "x11")] - pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint { - get_native_visual_id(self.display, self.config_id) - } - - pub fn finish(self, nwin: ffi::EGLNativeWindowType) -> Result { - let egl = EGL.as_ref().unwrap(); - let surface = unsafe { - let surface = - egl.CreateWindowSurface(self.display, self.config_id, nwin, std::ptr::null()); - if surface.is_null() { - return Err(CreationError::OsError("eglCreateWindowSurface failed".to_string())); - } - surface - }; - - self.finish_impl(Some(surface)) - } - - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - pub fn finish_surfaceless(self) -> Result { - // FIXME: Also check for the GL_OES_surfaceless_context *CONTEXT* - // extension - if self.extensions.iter().find(|s| s == &"EGL_KHR_surfaceless_context").is_none() { - Err(CreationError::NotSupported("EGL surfaceless not supported".to_string())) - } else { - self.finish_impl(None) - } - } - - #[cfg(any( - target_os = "android", - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - pub fn finish_pbuffer(self, size: dpi::PhysicalSize) -> Result { - let size: (u32, u32) = size.into(); - - let egl = EGL.as_ref().unwrap(); - let tex_fmt = if self.pixel_format.alpha_bits > 0 { - ffi::egl::TEXTURE_RGBA - } else { - ffi::egl::TEXTURE_RGB - }; - let attrs = &[ - ffi::egl::WIDTH as raw::c_int, - size.0 as raw::c_int, - ffi::egl::HEIGHT as raw::c_int, - size.1 as raw::c_int, - ffi::egl::NONE as raw::c_int, - ]; - - let surface = unsafe { - let surface = egl.CreatePbufferSurface(self.display, self.config_id, attrs.as_ptr()); - if surface.is_null() || surface == ffi::egl::NO_SURFACE { - return Err(CreationError::OsError("eglCreatePbufferSurface failed".to_string())); - } - surface - }; - - self.finish_impl(Some(surface)) - } - - fn finish_impl( - self, - surface: Option, - ) -> Result { - let share = match self.opengl.sharing { - Some(ctx) => ctx.context, - None => std::ptr::null(), - }; - - let context = unsafe { - if let Some(version) = self.version { - create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - version, - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - )? - } else if self.api == Api::OpenGlEs { - if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (2, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (1, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - } else { - if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (3, 2), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (3, 1), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (1, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - } - }; - - if let Some(surface) = surface { - // VSync defaults to enabled; disable it if it was not requested. - if !self.opengl.vsync { - let _guard = MakeCurrentGuard::new(self.display, surface, surface, context) - .map_err(|err| CreationError::OsError(err))?; - - let egl = EGL.as_ref().unwrap(); - unsafe { - if egl.SwapInterval(self.display, 0) == ffi::egl::FALSE { - panic!("finish_impl: eglSwapInterval failed: 0x{:x}", egl.GetError()); - } - } - } - } - - Ok(Context { - display: self.display, - context, - surface: surface.map(|s| Mutex::new(s)), - api: self.api, - pixel_format: self.pixel_format, - }) +impl DerefMut for Egl { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } -unsafe fn choose_fbconfig( - display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - api: Api, - version: Option<(u8, u8)>, - pf_reqs: &PixelFormatRequirements, - surface_type: SurfaceType, - opengl: &GlAttributes<&Context>, - mut config_selector: F, -) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> -where - F: FnMut( - Vec, - ffi::egl::types::EGLDisplay, - ) -> Result, -{ +// Obtain the error from the EGL. +fn check_error() -> Result<()> { let egl = EGL.as_ref().unwrap(); - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - if egl_version >= &(1, 2) { - out.push(ffi::egl::COLOR_BUFFER_TYPE as raw::c_int); - out.push(ffi::egl::RGB_BUFFER as raw::c_int); - } - - out.push(ffi::egl::SURFACE_TYPE as raw::c_int); - let surface_type = match surface_type { - SurfaceType::Window => ffi::egl::WINDOW_BIT, - SurfaceType::PBuffer => ffi::egl::PBUFFER_BIT, - SurfaceType::Surfaceless => 0, - }; - out.push(surface_type as raw::c_int); - - match (api, version) { - (Api::OpenGlEs, Some((3, _))) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int); - } - (Api::OpenGlEs, Some((2, _))) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int); - } - (Api::OpenGlEs, _) => { - if egl_version >= &(1, 3) { - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int); - } - } - (Api::OpenGl, _) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_BIT as raw::c_int); - } - (_, _) => unimplemented!(), + unsafe { + let raw_code = egl.GetError() as egl::types::EGLenum; + let kind = match raw_code { + egl::SUCCESS => return Ok(()), + egl::NOT_INITIALIZED => ErrorKind::InitializationFailed, + egl::BAD_ACCESS => ErrorKind::BadAccess, + egl::BAD_ALLOC => ErrorKind::OutOfMemory, + egl::BAD_ATTRIBUTE => ErrorKind::BadAttribute, + egl::BAD_CONTEXT => ErrorKind::BadContext, + egl::BAD_CONFIG => ErrorKind::BadConfig, + egl::BAD_CURRENT_SURFACE => ErrorKind::BadCurrentSurface, + egl::BAD_DISPLAY => ErrorKind::BadDisplay, + egl::BAD_SURFACE => ErrorKind::BadSurface, + egl::BAD_MATCH => ErrorKind::BadMatch, + egl::BAD_PARAMETER => ErrorKind::BadParameter, + egl::BAD_NATIVE_PIXMAP => ErrorKind::BadNativePixmap, + egl::BAD_NATIVE_WINDOW => ErrorKind::BadNativeWindow, + egl::CONTEXT_LOST => ErrorKind::ContextLost, + code => unreachable!("unhandled EGL code {}", code), }; - if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated { - out.push(ffi::egl::CONFIG_CAVEAT as raw::c_int); - out.push(if hardware_accelerated { - ffi::egl::NONE as raw::c_int - } else { - ffi::egl::SLOW_CONFIG as raw::c_int - }); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(ffi::egl::RED_SIZE as raw::c_int); - out.push((color / 3) as raw::c_int); - out.push(ffi::egl::GREEN_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int); - out.push(ffi::egl::BLUE_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(ffi::egl::ALPHA_SIZE as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(ffi::egl::DEPTH_SIZE as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(ffi::egl::STENCIL_SIZE as raw::c_int); - out.push(stencil as raw::c_int); - } - - if let Some(true) = pf_reqs.double_buffer { - return Err(CreationError::NoAvailablePixelFormat); - } - - if let Some(multisampling) = pf_reqs.multisampling { - out.push(ffi::egl::SAMPLES as raw::c_int); - out.push(multisampling as raw::c_int); - } - - if pf_reqs.stereoscopy { - return Err(CreationError::NoAvailablePixelFormat); - } - - if let Some(xid) = pf_reqs.x11_visual_xid { - out.push(ffi::egl::NATIVE_VISUAL_ID as raw::c_int); - out.push(xid as raw::c_int); - } - - // FIXME: srgb is not taken into account - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - // TODO: with EGL you need to manually set the behavior - unimplemented!() - } - } - - out.push(ffi::egl::NONE as raw::c_int); - out - }; - - // calling `eglChooseConfig` - let mut num_configs = std::mem::zeroed(); - if egl.ChooseConfig(display, descriptor.as_ptr(), std::ptr::null_mut(), 0, &mut num_configs) - == 0 - { - return Err(CreationError::OsError("eglChooseConfig failed".to_string())); - } - - if num_configs == 0 { - return Err(CreationError::NoAvailablePixelFormat); - } - - let mut config_ids = Vec::with_capacity(num_configs as usize); - config_ids.resize_with(num_configs as usize, || std::mem::zeroed()); - if egl.ChooseConfig( - display, - descriptor.as_ptr(), - config_ids.as_mut_ptr(), - num_configs, - &mut num_configs, - ) == 0 - { - return Err(CreationError::OsError("eglChooseConfig failed".to_string())); - } - - // We're interested in those configs which allow our desired VSync. - let desired_swap_interval = if opengl.vsync { 1 } else { 0 }; - - let config_ids = config_ids - .into_iter() - .filter(|&config| { - let mut min_swap_interval = 0; - let res = egl.GetConfigAttrib( - display, - config, - ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint, - &mut min_swap_interval, - ); - - if desired_swap_interval < min_swap_interval { - return false; - } - - let mut max_swap_interval = 0; - let res = egl.GetConfigAttrib( - display, - config, - ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint, - &mut max_swap_interval, - ); - - if desired_swap_interval > max_swap_interval { - return false; - } - - true - }) - .collect::>(); - - if config_ids.is_empty() { - return Err(CreationError::NoAvailablePixelFormat); - } - - let config_id = - config_selector(config_ids, display).map_err(|_| CreationError::NoAvailablePixelFormat)?; - - // analyzing each config - macro_rules! attrib { - ($egl:expr, $display:expr, $config:expr, $attr:expr) => {{ - let mut value = std::mem::zeroed(); - let res = $egl.GetConfigAttrib( - $display, - $config, - $attr as ffi::egl::types::EGLint, - &mut value, - ); - if res == 0 { - return Err(CreationError::OsError("eglGetConfigAttrib failed".to_string())); - } - value - }}; - } - - let desc = PixelFormat { - hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, - color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 - + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 - + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8, - alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8, - depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8, - stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8, - stereoscopy: false, - double_buffer: true, - multisampling: match attrib!(egl, 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 - }; - - Ok((config_id, desc)) -} - -unsafe fn create_context( - display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - extensions: &[String], - api: Api, - version: (u8, u8), - config_id: ffi::egl::types::EGLConfig, - gl_debug: bool, - gl_robustness: Robustness, - share: ffi::EGLContext, -) -> Result { - let egl = EGL.as_ref().unwrap(); - - let mut context_attributes = Vec::with_capacity(10); - let mut flags = 0; - - if egl_version >= &(1, 5) - || extensions.iter().find(|s| s == &"EGL_KHR_create_context").is_some() - { - context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); - context_attributes.push(version.0 as i32); - context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); - context_attributes.push(version.1 as i32); - - // handling robustness - let supports_robustness = egl_version >= &(1, 5) - || extensions.iter().find(|s| s == &"EGL_EXT_create_context_robustness").is_some(); - - match gl_robustness { - Robustness::NotRobust => (), - - Robustness::NoError => { - if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as raw::c_int); - context_attributes.push(1); - } - } - - Robustness::RobustNoResetNotification => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } else { - return Err(CreationError::RobustnessNotSupported); - } - } - - Robustness::TryRobustNoResetNotification => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } - } - - Robustness::RobustLoseContextOnReset => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } else { - return Err(CreationError::RobustnessNotSupported); - } - } - - Robustness::TryRobustLoseContextOnReset => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } - } - } - - if gl_debug { - if egl_version >= &(1, 5) { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); - context_attributes.push(ffi::egl::TRUE as i32); - } - - // TODO: using this flag sometimes generates an error - // there was a change in the specs that added this flag, so it - // may not be supported everywhere ; however it is - // not possible to know whether it is supported or - // not flags = flags | - // ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; - } - - // In at least some configurations, the Android emulator’s GL - // implementation advertises support for the - // EGL_KHR_create_context extension but returns BAD_ATTRIBUTE - // when CONTEXT_FLAGS_KHR is used. - if flags != 0 { - context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); - context_attributes.push(flags); - } - } else if egl_version >= &(1, 3) && api == Api::OpenGlEs { - // robustness is not supported - match gl_robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - - context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); - context_attributes.push(version.0 as i32); - } - - context_attributes.push(ffi::egl::NONE as i32); - - let context = egl.CreateContext(display, config_id, share, context_attributes.as_ptr()); - - if context.is_null() { - match egl.GetError() as u32 { - ffi::egl::BAD_MATCH | ffi::egl::BAD_ATTRIBUTE => { - return Err(CreationError::OpenGlVersionNotSupported); - } - e => panic!("create_context: eglCreateContext failed: 0x{:x}", e), - } + Err(Error::new(raw_code as i64, None, kind)) } - - Ok(context) } diff --git a/glutin/src/api/egl/surface.rs b/glutin/src/api/egl/surface.rs new file mode 100644 index 00000000000..625072d1fe4 --- /dev/null +++ b/glutin/src/api/egl/surface.rs @@ -0,0 +1,364 @@ +#![allow(clippy::vec_init_then_push)] + +// TODO add creation checks +use std::ffi; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::{EGLAttrib, EGLConfig, EGLDisplay, EGLSurface, EGLint}; + +use super::Egl; + +use raw_window_handle::RawWindowHandle; + +use crate::error::{ErrorKind, Result}; +use crate::surface::{ + AsRawSurface, DamageRect, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, + SurfaceTypeTrait, WindowSurface, +}; + +use super::config::Config; +use super::display::Display; + +impl Display { + pub fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let width = surface_attributes.width.unwrap(); + let height = surface_attributes.height.unwrap(); + + // XXX Window surface is using `EGLAttrib` and not `EGLint`. + let mut attrs = Vec::::new(); + + // Add dimensions. + attrs.push(egl::WIDTH as EGLAttrib); + attrs.push(width.get() as EGLAttrib); + + attrs.push(egl::HEIGHT as EGLAttrib); + attrs.push(height.get() as EGLAttrib); + + // Add information about render buffer. + attrs.push(egl::RENDER_BUFFER as EGLAttrib); + let buffer = + if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER } + as EGLAttrib; + attrs.push(buffer); + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + unsafe { + let surface = Self::check_surface_error(self.egl.CreatePbufferSurface( + self.display, + config.config, + attrs.as_ptr() as *const _, + ))?; + + Ok(Surface { + egl: self.egl, + display: self.display, + native_window: None, + config: config.config, + surface, + _ty: PhantomData, + }) + } + } + + pub fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); + + let mut attrs = Vec::::new(); + + if surface_attributes.srgb.is_some() + && self.client_extensions.contains("EGL_KHR_gl_colorspace") + { + attrs.push(egl::GL_COLORSPACE as EGLAttrib); + let colorspace = match surface_attributes.srgb { + Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, + _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, + }; + attrs.push(colorspace); + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + unsafe { + let surface = Self::check_surface_error(self.egl.CreatePlatformPixmapSurface( + self.display, + config.config, + native_pixmap.raw(), + attrs.as_ptr(), + ))?; + + Ok(Surface { + egl: self.egl, + display: self.display, + native_window: None, + config: config.config, + surface, + _ty: PhantomData, + }) + } + } + + pub fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + // Create native window. + let native_window = NativeWindow::new( + surface_attributes.width.unwrap(), + surface_attributes.height.unwrap(), + surface_attributes.raw_window_handle.as_ref().unwrap(), + )?; + + // XXX Window surface is using `EGLAttrib` and not `EGLint`. + let mut attrs = Vec::::new(); + + // Add information about render buffer. + attrs.push(egl::RENDER_BUFFER as EGLAttrib); + let buffer = + if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER } + as EGLAttrib; + attrs.push(buffer); + + // // Add colorspace if the extension is present. + if surface_attributes.srgb.is_some() + && self.client_extensions.contains("EGL_KHR_gl_colorspace") + { + attrs.push(egl::GL_COLORSPACE as EGLAttrib); + let colorspace = match surface_attributes.srgb { + Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, + _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, + }; + attrs.push(colorspace); + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + unsafe { + let surface = Self::check_surface_error(self.egl.CreatePlatformWindowSurface( + self.display, + config.config, + native_window.raw(), + attrs.as_ptr() as *const _, + ))?; + + Ok(Surface { + egl: self.egl, + display: self.display, + native_window: Some(native_window), + config: config.config, + surface, + _ty: PhantomData, + }) + } + } + + fn check_surface_error(surface: EGLSurface) -> Result { + if surface == egl::NO_SURFACE { + Err(super::check_error().err().unwrap()) + } else { + Ok(surface) + } + } +} + +pub struct Surface { + egl: &'static Egl, + display: EGLDisplay, + config: EGLConfig, + native_window: Option, + pub(crate) surface: EGLSurface, + _ty: PhantomData, +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + RawSurface::Egl(self.surface) + } +} + +impl Surface { + pub fn buffer_age(&self) -> u32 { + self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) as u32 + } + + pub fn width(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + pub fn height(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + pub fn is_single_buffered(&self) -> bool { + self.raw_attribute(egl::RENDER_BUFFER as EGLint) != egl::SINGLE_BUFFER as i32 + } + + pub fn swap_buffers(&self) -> Result<()> { + unsafe { + if self.egl.SwapBuffers(self.display, self.surface) == egl::FALSE { + super::check_error() + } else { + Ok(()) + } + } + } + + pub fn swap_buffers_with_damage(&self, rects: &[DamageRect]) -> Result<()> { + unsafe { + if self.egl.SwapBuffersWithDamageKHR( + self.display, + self.surface, + rects.as_ptr() as *mut _, + (rects.len() * 4) as _, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + pub fn is_current(&self) -> bool { + self.is_current_draw() && self.is_current_read() + } + + pub fn is_current_draw(&self) -> bool { + unsafe { self.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.surface } + } + + pub fn is_current_read(&self) -> bool { + unsafe { self.egl.GetCurrentSurface(egl::READ as EGLint) == self.surface } + } + + fn raw_attribute(&self, attr: EGLint) -> EGLint { + unsafe { + let mut value = 0; + self.egl.QuerySurface(self.display, self.surface, attr, &mut value); + value + } + } +} + +impl Surface { + pub fn resize(&self, width: NonZeroU32, height: NonZeroU32) { + self.native_window.as_ref().unwrap().resize(width, height) + } +} + +enum NativeWindow { + #[cfg(all( + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ), + feature = "wayland" + ))] + Wayland(wayland_egl::WlEglSurface), + + Xlib(u64), + + Android(*mut ffi::c_void), + + Win32(*mut ffi::c_void), + + Gbm(*mut ffi::c_void), +} + +impl NativeWindow { + pub(crate) fn new( + _width: NonZeroU32, + _height: NonZeroU32, + raw_window_handle: &RawWindowHandle, + ) -> Result { + let native_window = match raw_window_handle { + #[cfg(all( + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ), + feature = "wayland" + ))] + RawWindowHandle::Wayland(window_handle) => { + let wl_surface = window_handle.surface; + let wl_egl_surface = unsafe { + wayland_egl::WlEglSurface::new_from_raw( + wl_surface.cast(), + _width.get() as i32, + _height.get() as i32, + ) + }; + Self::Wayland(wl_egl_surface) + } + RawWindowHandle::Xlib(window_handle) => Self::Xlib(window_handle.window as u64), + RawWindowHandle::AndroidNdk(window_handle) => { + Self::Android(window_handle.a_native_window) + } + RawWindowHandle::Win32(window_hanlde) => Self::Win32(window_hanlde.hwnd), + RawWindowHandle::Gbm(window_handle) => Self::Gbm(window_handle.gbm_surface), + _ => return Err(ErrorKind::NotSupported.into()), + }; + + Ok(native_window) + } + + pub(crate) fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { + match self { + #[cfg(all( + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ), + feature = "wayland" + ))] + Self::Wayland(wl_egl_surface) => { + wl_egl_surface.resize(_width.get() as _, _height.get() as _, 0, 0) + } + Self::Xlib(_) => (), + Self::Android(_) => (), + Self::Win32(_) => (), + Self::Gbm(_) => (), + } + } + + pub(crate) fn raw(&self) -> *mut ffi::c_void { + match self { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[cfg(feature = "wayland")] + Self::Wayland(wl_egl_surface) => wl_egl_surface.ptr() as *mut _, + Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void, + Self::Win32(hwnd) => *hwnd, + Self::Android(a_native_window) => *a_native_window, + Self::Gbm(gbm_surface) => *gbm_surface, + } + } +} diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs new file mode 100644 index 00000000000..665a3ba7fa1 --- /dev/null +++ b/glutin/src/api/glx/config.rs @@ -0,0 +1,250 @@ +use std::collections::HashSet; +use std::os::raw::c_int; +use std::slice; +use std::sync::Arc; + +use glutin_glx_sys::glx; +use glutin_glx_sys::glx::types::GLXFBConfig; +use glutin_glx_sys::glx_extra; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig, +}; +use crate::platform_impl::x11::{X11VisualInfo, XLIB}; + +use super::display::Display; +use super::{Glx, GlxDisplay}; + +impl Display { + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + let mut config_attributes = Vec::::new(); + + if !template.api.contains(Api::OPENGL) { + return None; + } + + // Add color buffer type. + match template.color_buffer_type { + ColorBufferType::Rgb { r_size, g_size, b_size } => { + // Type. + config_attributes.push(glx::X_VISUAL_TYPE as c_int); + config_attributes.push(glx::TRUE_COLOR as c_int); + + // R. + config_attributes.push(glx::RED_SIZE as c_int); + config_attributes.push(r_size as c_int); + + // G. + config_attributes.push(glx::GREEN_SIZE as c_int); + config_attributes.push(g_size as c_int); + + // B. + config_attributes.push(glx::BLUE_SIZE as c_int); + config_attributes.push(b_size as c_int); + } + ColorBufferType::Luminance(luminance) => { + // Type. + config_attributes.push(glx::X_VISUAL_TYPE as c_int); + config_attributes.push(glx::GRAY_SCALE as c_int); + + // L. + config_attributes.push(glx::RED_SIZE as c_int); + config_attributes.push(luminance as c_int); + } + }; + + // Double buffer. + config_attributes.push(glx::DOUBLEBUFFER as c_int); + config_attributes.push(!template.single_buffering as c_int); + + // Add alpha. + config_attributes.push(glx::ALPHA_SIZE as c_int); + config_attributes.push(template.alpha_size as c_int); + + // Add depth. + config_attributes.push(glx::DEPTH_SIZE as c_int); + config_attributes.push(template.depth_size as c_int); + + // Add stencil. + config_attributes.push(glx::STENCIL_SIZE as c_int); + config_attributes.push(template.stencil_size as c_int); + + // Add surface type. + config_attributes.push(glx::DRAWABLE_TYPE as c_int); + let mut surface_type = 0; + if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { + surface_type |= glx::WINDOW_BIT; + } + if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { + surface_type |= glx::PBUFFER_BIT; + } + if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { + surface_type |= glx::PIXMAP_BIT; + } + config_attributes.push(surface_type as c_int); + + // Add maximum height of pbuffer. + if let Some(pbuffer_width) = template.max_pbuffer_width { + config_attributes.push(glx::MAX_PBUFFER_WIDTH as c_int); + config_attributes.push(pbuffer_width as c_int); + } + + // Add maximum width of pbuffer. + if let Some(pbuffer_height) = template.max_pbuffer_height { + config_attributes.push(glx::MAX_PBUFFER_HEIGHT as c_int); + config_attributes.push(pbuffer_height as c_int); + } + + // Add stereoscopy, if present. + if let Some(stereoscopy) = template.stereoscopy { + config_attributes.push(glx::STEREO as c_int); + config_attributes.push(stereoscopy as c_int); + } + + // TODO, this requires extension for GLX 1.3 Add samples. Otherwise it's supported + // above GLX 1.4. + config_attributes.push(glx::SAMPLE_BUFFERS as c_int); + config_attributes.push(template.sample_buffers as c_int); + + // Push `glx::NONE` to terminate the list. + config_attributes.push(glx::NONE as c_int); + + unsafe { + let mut num_configs = 0; + let raw_configs = self.glx.ChooseFBConfig( + self.display.0, + self.screen as _, + config_attributes.as_ptr() as *const _, + &mut num_configs, + ); + + if raw_configs.is_null() { + return None; + } + + let configs = slice::from_raw_parts_mut(raw_configs, num_configs as usize).to_vec(); + + // Free the memory from the Xlib, since we've just copied it. + (XLIB.as_ref().unwrap().XFree)(raw_configs as *mut _); + + let iter = configs + .into_iter() + .map(|raw| Config { + glx: self.glx, + display: self.display, + config: raw, + client_extensions: self.client_extensions.clone(), + }) + .filter(move |config| { + if template.transparency { + config.x11_visual().unwrap().supports_transparency() + } else { + true + } + }); + + Some(Box::new(iter)) + } + } +} + +#[derive(Clone)] +pub struct Config { + glx: &'static Glx, + display: GlxDisplay, + pub(crate) config: GLXFBConfig, + client_extensions: Arc>, +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + RawConfig::Glx(self.config) + } +} + +impl Config { + pub fn color_buffer_type(&self) -> ColorBufferType { + match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ { + glx::TRUE_COLOR => { + let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8; + let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8; + let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8; + ColorBufferType::Rgb { r_size, g_size, b_size } + } + glx::GRAY_SCALE => { + let luma = self.raw_attribute(glx::RED_SIZE as c_int); + ColorBufferType::Luminance(luma as u8) + } + _ => unimplemented!(), + } + } + + pub fn x11_visual(&self) -> Option { + unsafe { + let raw_visual = self.glx.GetVisualFromFBConfig(self.display.0, self.config); + Some(X11VisualInfo::from_raw(self.display.0 as *mut _, raw_visual as *mut _)) + } + } + + pub fn native_visual(&self) -> u32 { + self.raw_attribute(glx::VISUAL_ID as c_int) as u32 + } + + pub fn alpha_size(&self) -> u8 { + self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 + } + + pub fn srgb_capable(&self) -> bool { + if self.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 + } else if self.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + } else { + false + } + } + + pub fn depth_size(&self) -> u8 { + self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 + } + + pub fn stencil_size(&self) -> u8 { + self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 + } + + pub fn sample_buffers(&self) -> u8 { + self.raw_attribute(glx::SAMPLE_BUFFERS as c_int) as u8 + } + + pub fn config_surface_types(&self) -> ConfigSurfaceTypes { + let mut ty = ConfigSurfaceTypes::empty(); + + let raw_ty = self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32; + if raw_ty & glx::WINDOW_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::WINDOW); + } + if raw_ty & glx::PBUFFER_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PBUFFER); + } + if raw_ty & glx::PIXMAP_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PIXMAP); + } + + ty + } + + pub fn api(&self) -> Api { + Api::OPENGL + } + + fn raw_attribute(&self, attr: c_int) -> c_int { + unsafe { + let mut val = 0; + self.glx.GetFBConfigAttrib(self.display.0, self.config, attr, &mut val); + val as c_int + } + } +} diff --git a/glutin/src/api/glx/context.rs b/glutin/src/api/glx/context.rs new file mode 100644 index 00000000000..3d70d577ebe --- /dev/null +++ b/glutin/src/api/glx/context.rs @@ -0,0 +1,176 @@ +#![allow(clippy::vec_init_then_push)] +use std::cell::Cell; +use std::ffi::{self, CStr}; +use std::marker::PhantomData; +use std::os::raw::c_int; +use std::rc::Rc; + +use glutin_glx_sys::glx; +use glutin_glx_sys::glx::types::GLXContext; + +use crate::context::{AsRawContext, ContextAttributes, RawContext}; +use crate::error::{ErrorKind, Result}; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; +use super::{Glx, GlxDisplay}; + +impl Display { + pub fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let mut attrs = Vec::::new(); + attrs.push(glx::NONE as c_int); + + let shared_context = + if let Some(shared_context) = context_attributes.shared_context.as_ref() { + match shared_context { + RawContext::Glx(shared_context) => *shared_context, + _ => return Err(ErrorKind::NotSupported.into()), + } + } else { + std::ptr::null() + }; + + unsafe { + let context = self.glx.CreateNewContext( + self.display.0, + config.config, + glx::RGBA_TYPE as c_int, + shared_context, + // Direct context. + 1, + ); + + super::last_glx_error(self.display)?; + + let inner = ContextInner { glx: self.glx, display: self.display, context }; + + Ok(NotCurrentContext::new(inner)) + } + } +} + +struct ContextInner { + glx: &'static Glx, + display: GlxDisplay, + context: GLXContext, +} + +impl Drop for ContextInner { + fn drop(&mut self) { + unsafe { + self.glx.DestroyContext(self.display.0, self.context); + } + } +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + unsafe { + self.glx.MakeContextCurrent( + self.display.0, + surface_draw.surface, + surface_read.surface, + self.context, + ); + } + } + + fn make_not_current(&self) { + unsafe { + self.glx.MakeContextCurrent(self.display.0, 0, 0, std::ptr::null()); + } + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread. + _nosendsync: PhantomData>, +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Glx(self.inner.context) + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Glx(self.inner.context) + } +} + +impl PossiblyCurrentContext { + pub fn make_not_current(self) -> NotCurrentContext { + self.inner.make_not_current(); + NotCurrentContext::new(self.inner) + } + + pub fn make_current(&self, surface: &Surface) { + self.inner.make_current_draw_read(surface, surface) + } + + pub fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + self.inner.make_current_draw_read(surface_draw, surface_read) + } + + pub fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + pub fn is_current(&self) -> bool { + unsafe { self.inner.glx.GetCurrentContext() == self.inner.context } + } + + pub fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { self.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ } + } +} + +pub struct NotCurrentContext { + inner: ContextInner, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner, _nosync: PhantomData } + } + + pub fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } + + pub fn make_current(self, surface: &Surface) -> PossiblyCurrentContext { + self.inner.make_current_draw_read(surface, surface); + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } + + pub fn make_current_draw_read( + self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> PossiblyCurrentContext { + self.inner.make_current_draw_read(surface_draw, surface_read); + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs new file mode 100644 index 00000000000..2a76f863e8d --- /dev/null +++ b/glutin/src/api/glx/display.rs @@ -0,0 +1,106 @@ +use std::collections::HashSet; +use std::ffi::CStr; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use raw_window_handle::RawDisplayHandle; + +use glutin_glx_sys::glx; + +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; + +use super::{Glx, GlxDisplay, XlibErrorHookRegistrator, GLX, GLX_BASE_ERROR}; + +#[derive(Default, Clone, Copy)] +pub(crate) struct GlxVersion { + pub(crate) major: i32, + pub(crate) minor: i32, +} + +pub struct Display { + pub(crate) glx: &'static Glx, + pub(crate) display: GlxDisplay, + pub(crate) screen: i32, + /// Client GLX extensions. + pub(crate) client_extensions: Arc>, +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Glx(self.display.0 as *const _) + } +} + +impl Display { + /// Create GLX display. + /// + /// # Safety + /// + /// The `display` must point to the valid Xlib display. + pub unsafe fn from_raw( + display: RawDisplayHandle, + error_hook_registrator: XlibErrorHookRegistrator, + ) -> Result { + // Don't load GLX when unsupported platform was requested. + let (display, screen) = match display { + RawDisplayHandle::Xlib(handle) => { + (GlxDisplay(handle.display as *mut _), handle.screen as i32) + } + _ => return Err(ErrorKind::NotSupported.into()), + }; + + let glx = match GLX.as_ref() { + Some(glx) => glx, + None => return Err(ErrorKind::NotFound.into()), + }; + + // Set the base for errors comming from glx. + let mut error_base = 0; + let mut event_base = 0; + if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { + // The glx extension isn't present. + return Err(ErrorKind::InitializationFailed.into()); + } + GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); + + // This is completely ridiculous, but VirtualBox's OpenGL driver needs + // some call handled by *it* (i.e. not Mesa) to occur before + // anything else can happen. That is because VirtualBox's OpenGL + // driver is going to apply binary patches to Mesa in the DLL + // constructor and until it's loaded it won't have a chance to do that. + // + // The easiest way to do this is to just call `glXQueryVersion()` before + // doing anything else. See: https://www.virtualbox.org/ticket/8293 + let mut version = GlxVersion::default(); + if glx.QueryVersion(display.0, &mut version.major, &mut version.minor) == 0 { + return Err(ErrorKind::InitializationFailed.into()); + } + + if version.major < 1 && version.minor < 3 { + return Err(ErrorKind::NotSupported.into()); + } + + // Register the error handling hook. + error_hook_registrator(Box::new(super::glx_error_hook)); + + let client_extensions = Arc::new(get_extensions(glx, display)); + + Ok(Self { display, glx, screen, client_extensions }) + } +} + +fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> { + unsafe { + let extensions = glx.GetClientString(display.0, glx::EXTENSIONS as i32); + if extensions.is_null() { + return HashSet::new(); + } + + if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { + extensions.split(' ').collect::>() + } else { + HashSet::new() + } + } +} diff --git a/glutin/src/api/glx/make_current_guard.rs b/glutin/src/api/glx/make_current_guard.rs deleted file mode 100644 index 67366b63a3b..00000000000 --- a/glutin/src/api/glx/make_current_guard.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::platform::unix::x11::XConnection; -use glutin_glx_sys as ffi; - -use std::sync::Arc; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct MakeCurrentGuard { - old_display: *mut ffi::Display, - display: *mut ffi::Display, - xconn: Arc, - possibly_invalid: Option, -} - -#[derive(Debug)] -struct MakeCurrentGuardInner { - old_drawable: ffi::glx::types::GLXDrawable, - old_context: ffi::GLXContext, -} - -impl MakeCurrentGuard { - pub fn new( - xconn: &Arc, - drawable: ffi::glx::types::GLXDrawable, - context: ffi::GLXContext, - ) -> Result { - unsafe { - let glx = super::GLX.as_ref().unwrap(); - - let ret = MakeCurrentGuard { - old_display: glx.GetCurrentDisplay() as *mut _, - display: xconn.display as *mut _, - xconn: Arc::clone(xconn), - possibly_invalid: Some(MakeCurrentGuardInner { - old_drawable: glx.GetCurrentDrawable(), - old_context: glx.GetCurrentContext(), - }), - }; - - let res = glx.MakeCurrent(xconn.display as *mut _, drawable, context); - - if res == 0 { - let err = xconn.check_errors(); - Err(format!("`glXMakeCurrent` failed: {:?}", err)) - } else { - Ok(ret) - } - } - } - - pub fn old_context(&mut self) -> Option { - self.possibly_invalid.as_ref().map(|pi| pi.old_context) - } - - pub fn invalidate(&mut self) { - self.possibly_invalid.take(); - } -} - -impl Drop for MakeCurrentGuard { - fn drop(&mut self) { - let glx = super::GLX.as_ref().unwrap(); - let (drawable, context) = match self.possibly_invalid.take() { - Some(inner) => (inner.old_drawable, inner.old_context), - None => (0, std::ptr::null()), - }; - - let display = match self.old_display { - old_display if old_display == std::ptr::null_mut() => self.display, - old_display => old_display, - }; - - let res = unsafe { glx.MakeCurrent(display as *mut _, drawable, context) }; - - if res == 0 { - let err = self.xconn.check_errors(); - panic!("`glXMakeCurrent` failed: {:?}", err); - } - } -} diff --git a/glutin/src/api/glx/mod.rs b/glutin/src/api/glx/mod.rs index 3367b7bb965..2426bf914a8 100644 --- a/glutin/src/api/glx/mod.rs +++ b/glutin/src/api/glx/mod.rs @@ -1,781 +1,175 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#![cfg(feature = "x11")] +use std::ffi::{self, CStr, CString}; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Mutex; -mod make_current_guard; -mod glx { - use crate::api::dlloader::{SymTrait, SymWrapper}; - use glutin_glx_sys as ffi; - use std::ops::{Deref, DerefMut}; +use libloading::Library; +use once_cell::sync::Lazy; +use x11_dl::xlib::{self, XErrorEvent}; - #[derive(Clone)] - pub struct Glx(SymWrapper); +use glutin_glx_sys::glx; +use glutin_glx_sys::glx::types::Display as GLXDisplay; +use glutin_glx_sys::glx_extra; - /// Because `*const raw::c_void` doesn't implement `Sync`. - unsafe impl Sync for Glx {} +use crate::error::{Error, ErrorKind, Result}; +use crate::lib_loading::{SymLoading, SymWrapper}; +use crate::platform_impl::x11::XLIB; - impl SymTrait for ffi::glx::Glx { - fn load_with(lib: &libloading::Library) -> Self { - Self::load_with(|sym| unsafe { - lib.get(std::ffi::CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) - .map(|sym| *sym) - .unwrap_or(std::ptr::null_mut()) - }) - } - } - - impl Glx { - pub fn new() -> Result { - let paths = vec!["libGL.so.1", "libGL.so"]; - - SymWrapper::new(paths).map(|i| Glx(i)) - } - } - - impl Deref for Glx { - type Target = ffi::glx::Glx; - - fn deref(&self) -> &ffi::glx::Glx { - &self.0 - } - } - - impl DerefMut for Glx { - fn deref_mut(&mut self) -> &mut ffi::glx::Glx { - &mut self.0 - } - } -} - -pub use self::glx::Glx; -use self::make_current_guard::MakeCurrentGuard; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; - -use crate::platform::unix::x11::XConnection; -use crate::platform_impl::x11_utils::SurfaceType; -use glutin_glx_sys as ffi; -use winit::dpi; - -use std::ffi::{CStr, CString}; -use std::os::raw; -use std::sync::Arc; - -lazy_static! { - pub static ref GLX: Option = Glx::new().ok(); -} +pub mod config; +pub mod context; +pub mod display; +pub mod surface; -#[derive(Debug)] -pub struct Context { - xconn: Arc, - drawable: ffi::Window, - context: ffi::GLXContext, - pixel_format: PixelFormat, -} +pub type XlibErrorHookRegistrator = + Box bool + Send + Sync>)>; -impl Context { - // transparent is `None` if window is raw. - pub fn new<'a>( - xconn: Arc, - pf_reqs: &PixelFormatRequirements, - opengl: &'a GlAttributes<&'a Context>, - screen_id: raw::c_int, - surface_type: SurfaceType, - transparent: Option, - ) -> Result, CreationError> { - let glx = GLX.as_ref().unwrap(); - // This is completely ridiculous, but VirtualBox's OpenGL driver needs - // some call handled by *it* (i.e. not Mesa) to occur before - // anything else can happen. That is because VirtualBox's OpenGL - // driver is going to apply binary patches to Mesa in the DLL - // constructor and until it's loaded it won't have a chance to do that. - // - // The easiest way to do this is to just call `glXQueryVersion()` before - // doing anything else. See: https://www.virtualbox.org/ticket/8293 - let (mut major, mut minor) = (0, 0); - unsafe { - glx.QueryVersion(xconn.display as *mut _, &mut major, &mut minor); - } +/// The base used for GLX errors. +static GLX_BASE_ERROR: AtomicI32 = AtomicI32::new(0); - // loading the list of extensions - let extensions = load_extensions(&xconn, screen_id)?; +/// The last error arrived from glx normallized by `GLX_BASE_ERROR`. +static LAST_GLX_ERROR: Lazy>> = Lazy::new(|| Mutex::new(None)); - // finding the pixel format we want - let (fb_config, pixel_format, visual_infos) = unsafe { - choose_fbconfig(&extensions, &xconn, screen_id, pf_reqs, surface_type, transparent)? +/// Store the last error comming from GLX. +fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool { + let xerror = xerror_event as *mut XErrorEvent; + unsafe { + let code = (*xerror).error_code; + let glx_code = code as i32 - GLX_BASE_ERROR.load(Ordering::Relaxed); + + // Get the kind of the error. + let kind = match code as u8 { + xlib::BadValue => ErrorKind::BadAttribute, + xlib::BadMatch => ErrorKind::BadMatch, + xlib::BadWindow => ErrorKind::BadNativeWindow, + xlib::BadAlloc => ErrorKind::OutOfMemory, + xlib::BadPixmap => ErrorKind::BadPixmap, + xlib::BadAccess => ErrorKind::BadAccess, + _ if glx_code >= 0 => match glx_code as glx::types::GLenum { + glx::PROTO_BAD_CONTEXT => ErrorKind::BadContext, + glx::PROTO_BAD_CONTEXT_STATE => ErrorKind::BadContext, + glx::PROTO_BAD_CURRENT_DRAWABLE => ErrorKind::BadCurrentSurface, + glx::PROTO_BAD_CURRENT_WINDOW => ErrorKind::BadCurrentSurface, + glx::PROTO_BAD_FBCONFIG => ErrorKind::BadConfig, + glx::PROTO_BAD_PBUFFER => ErrorKind::BadPbuffer, + glx::PROTO_BAD_PIXMAP => ErrorKind::BadPixmap, + glx::PROTO_UNSUPPORTED_PRIVATE_REQUEST => ErrorKind::Misc, + glx::PROTO_BAD_DRAWABLE => ErrorKind::BadSurface, + glx::PROTO_BAD_WINDOW => ErrorKind::BadSurface, + glx::PROTO_BAD_CONTEXT_TAG => ErrorKind::Misc, + glx::PROTO_BAD_RENDER_REQUEST => ErrorKind::Misc, + glx::PROTO_BAD_LARGE_REQUEST => ErrorKind::Misc, + _ => return false, + }, + _ => return false, }; - Ok(ContextPrototype { - extensions, - xconn, - opengl, - fb_config, - visual_infos: unsafe { std::mem::transmute(visual_infos) }, - pixel_format, - }) - } - - unsafe fn check_make_current(&self, ret: Option) -> Result<(), ContextError> { - if ret == Some(0) { - let err = self.xconn.check_errors(); - Err(ContextError::OsError(format!("`glXMakeCurrent` failed: {:?}", err))) - } else { - Ok(()) - } - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - let res = glx.MakeCurrent(self.xconn.display as *mut _, self.drawable, self.context); - self.check_make_current(Some(res)) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - if self.drawable == glx.GetCurrentDrawable() || self.context == glx.GetCurrentContext() { - let res = glx.MakeCurrent(self.xconn.display as *mut _, 0, std::ptr::null()); - self.check_make_current(Some(res)) - } else { - self.check_make_current(None) - } - } + // Get the string from X11 error. + let mut buf = vec![0u8; 1024]; + (XLIB.as_ref().unwrap().XGetErrorText)( + _display as *mut _, + (*xerror).error_code as _, + buf.as_mut_ptr() as *mut _, + buf.len() as _, + ); + let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string(); - #[inline] - pub fn is_current(&self) -> bool { - let glx = GLX.as_ref().unwrap(); - unsafe { glx.GetCurrentContext() == self.context } - } + *LAST_GLX_ERROR.lock().unwrap() = Some(Error::new(code as _, Some(description), kind)); - #[inline] - pub fn get_api(&self) -> crate::Api { - crate::Api::OpenGl + true } +} - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::GLXContext { - self.context +/// Get the error from the X11. +fn last_glx_error(display: GlxDisplay) -> Result<()> { + unsafe { + // Force synchronization. + (XLIB.as_ref().unwrap().XSync)(display.0 as *mut _, 0); } - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let glx = GLX.as_ref().unwrap(); - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - unsafe { glx.GetProcAddress(addr as *const _) as *const _ } + // Reset and report last error. + let last_error = LAST_GLX_ERROR.lock().unwrap().take(); + match last_error { + Some(error) => Err(error), + None => Ok(()), } +} - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - unsafe { - glx.SwapBuffers(self.xconn.display as *mut _, self.drawable); - } - if let Err(err) = self.xconn.check_errors() { - Err(ContextError::OsError(format!("`glXSwapBuffers` failed: {:?}", err))) - } else { - Ok(()) - } - } +pub(crate) struct Glx(pub SymWrapper); - #[inline] - pub fn buffer_age(&self) -> u32 { - let glx = GLX.as_ref().unwrap(); +pub(crate) struct GlxExtra(glx_extra::Glx); - let mut buffer_age = 0; +#[derive(Clone, Copy)] +pub(crate) struct GlxDisplay(*mut GLXDisplay); - unsafe { - glx.QueryDrawable( - self.xconn.display as *mut _, - self.drawable, - ffi::glx_extra::BACK_BUFFER_AGE_EXT as i32, - &mut buffer_age, - ); - } +unsafe impl Sync for Glx {} +unsafe impl Sync for GlxExtra {} - buffer_age - } +static GLX: Lazy> = Lazy::new(|| { + #[cfg(not(windows))] + let paths = ["libGL.so.1", "libGL.so"]; - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() + match SymWrapper::new(&paths).map(Glx) { + Ok(glx) => Some(glx), + Err(_) => None, } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Drop for Context { - fn drop(&mut self) { - let glx = GLX.as_ref().unwrap(); - unsafe { - // See `drop` for `crate::api::egl::Context` for rationale. - let mut guard = MakeCurrentGuard::new(&self.xconn, self.drawable, self.context) - .map_err(|err| ContextError::OsError(err)) - .unwrap(); +}); - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(gl_finish_fn != std::ptr::null()); - let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn); - gl_finish_fn(); +static GLX_EXTRA: Lazy> = Lazy::new(|| { + let glx = GLX.as_ref()?; + let glx_extra = glx_extra::Glx::load_with(|proc_name| { + let c_str = CString::new(proc_name).unwrap(); + unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } + }); - if guard.old_context() == Some(self.context) { - guard.invalidate() - } - std::mem::drop(guard); + Some(GlxExtra(glx_extra)) +}); - glx.DestroyContext(self.xconn.display as *mut _, self.context); - } - } -} - -#[derive(Debug)] -pub struct ContextPrototype<'a> { - extensions: String, - xconn: Arc, - opengl: &'a GlAttributes<&'a Context>, - fb_config: ffi::glx::types::GLXFBConfig, - visual_infos: ffi::XVisualInfo, - pixel_format: PixelFormat, -} - -impl<'a> ContextPrototype<'a> { +impl GlxExtra { #[inline] - pub fn get_visual_infos(&self) -> &ffi::XVisualInfo { - &self.visual_infos - } - - // creating GL context - fn create_context(&self) -> Result<(ffi::glx_extra::Glx, ffi::GLXContext), CreationError> { - let glx = GLX.as_ref().unwrap(); - let share = match self.opengl.sharing { - Some(ctx) => ctx.context, - None => std::ptr::null(), - }; - - // loading the extra GLX functions - let extra_functions = ffi::glx_extra::Glx::load_with(|proc_name| { + pub fn new(glx: &Glx) -> Self { + GlxExtra(glx_extra::Glx::load_with(|proc_name| { let c_str = CString::new(proc_name).unwrap(); unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } - }); - - let context = match self.opengl.version { - GlRequest::Latest => { - let opengl_versions = [ - (4, 6), - (4, 5), - (4, 4), - (4, 3), - (4, 2), - (4, 1), - (4, 0), - (3, 3), - (3, 2), - (3, 1), - ]; - // Try all OpenGL versions in descending order because some - // non-compliant drivers don't return - // the latest supported version but the one requested - opengl_versions - .iter() - .find_map(|opengl_version| { - create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - *opengl_version, - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - ) - .ok() - }) - .map_or_else( - || { - create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (1, 0), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - ) - }, - Ok, - )? - } - GlRequest::Specific(Api::OpenGl, (major, minor)) => create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (major, minor), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - )?, - GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (major, minor), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - )?, - }; - - Ok((extra_functions, context)) + })) } +} - pub fn finish_pbuffer(self, size: dpi::PhysicalSize) -> Result { - let glx = GLX.as_ref().unwrap(); - let size: (u32, u32) = size.into(); - let (_extra_functions, context) = self.create_context()?; - - let attributes: Vec = vec![ - ffi::glx::PBUFFER_WIDTH as raw::c_int, - size.0 as raw::c_int, - ffi::glx::PBUFFER_HEIGHT as raw::c_int, - size.1 as raw::c_int, - 0, - ]; - - let pbuffer = unsafe { - glx.CreatePbuffer(self.xconn.display as *mut _, self.fb_config, attributes.as_ptr()) - }; - - Ok(Context { - xconn: self.xconn, - drawable: pbuffer, - context, - pixel_format: self.pixel_format, +impl SymLoading for glx::Glx { + fn load_with(lib: &Library) -> Self { + Self::load_with(|sym| unsafe { + lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) + .map(|sym| *sym) + .unwrap_or(std::ptr::null_mut()) }) } +} - pub fn finish(self, window: ffi::Window) -> Result { - let glx = GLX.as_ref().unwrap(); - let (extra_functions, context) = self.create_context()?; - - // vsync - let swap_mode = if self.opengl.vsync { 1 } else { 0 }; - - let _guard = MakeCurrentGuard::new(&self.xconn, window, context) - .map_err(|err| CreationError::OsError(err))?; - - if check_ext(&self.extensions, "GLX_EXT_swap_control") - && extra_functions.SwapIntervalEXT.is_loaded() - { - // this should be the most common extension - unsafe { - extra_functions.SwapIntervalEXT(self.xconn.display as *mut _, window, swap_mode); - } - - let mut swap = unsafe { std::mem::zeroed() }; - unsafe { - glx.QueryDrawable( - self.xconn.display as *mut _, - window, - ffi::glx_extra::SWAP_INTERVAL_EXT as i32, - &mut swap, - ); - } - - if swap != swap_mode as u32 { - return Err(CreationError::OsError(format!( - "Couldn't setup vsync: expected interval `{}` but got `{}`", - swap_mode, swap - ))); - } - } else if check_ext(&self.extensions, "GLX_MESA_swap_control") - && extra_functions.SwapIntervalMESA.is_loaded() - { - unsafe { - extra_functions.SwapIntervalMESA(swap_mode as u32); - } - } else if check_ext(&self.extensions, "GLX_SGI_swap_control") - && extra_functions.SwapIntervalSGI.is_loaded() - { - unsafe { - extra_functions.SwapIntervalSGI(swap_mode); - } - } else if self.opengl.vsync { - return Err(CreationError::OsError( - "Couldn't find any available vsync extension".to_string(), - )); - } +impl Deref for Glx { + type Target = glx::Glx; - Ok(Context { - xconn: self.xconn, - drawable: window, - context, - pixel_format: self.pixel_format, - }) + fn deref(&self) -> &Self::Target { + &self.0 } } -extern "C" fn x_error_callback(_dpy: *mut ffi::Display, _err: *mut ffi::XErrorEvent) -> i32 { - 0 -} - -fn create_context( - extra_functions: &ffi::glx_extra::Glx, - extensions: &str, - xlib: &ffi::Xlib, - version: (u8, u8), - profile: Option, - debug: bool, - robustness: Robustness, - share: ffi::GLXContext, - display: *mut ffi::Display, - fb_config: ffi::glx::types::GLXFBConfig, - visual_infos: &ffi::XVisualInfo, -) -> Result { - let glx = GLX.as_ref().unwrap(); - unsafe { - let old_callback = (xlib.XSetErrorHandler)(Some(x_error_callback)); - let context = if check_ext(extensions, "GLX_ARB_create_context") { - let mut attributes = Vec::with_capacity(9); - - attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(version.0 as raw::c_int); - attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(version.1 as raw::c_int); - - if let Some(profile) = profile { - let flag = match profile { - GlProfile::Compatibility => { - ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB - } - GlProfile::Core => ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, - }; - - attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(flag as raw::c_int); - } - - let flags = { - let mut flags = 0; - - // robustness - if check_ext(extensions, "GLX_ARB_create_context_robustness") { - match robustness { - Robustness::RobustNoResetNotification - | Robustness::TryRobustNoResetNotification => { - attributes.push( - ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes - .push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as raw::c_int); - flags = - flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::RobustLoseContextOnReset - | Robustness::TryRobustLoseContextOnReset => { - attributes.push( - ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes - .push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as raw::c_int); - flags = - flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::NotRobust => (), - Robustness::NoError => (), - } - } else { - match robustness { - Robustness::RobustNoResetNotification - | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - } - - if debug { - flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int; - } - - flags - }; - - attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as raw::c_int); - attributes.push(flags); - - attributes.push(0); - - extra_functions.CreateContextAttribsARB( - display as *mut _, - fb_config, - share, - 1, - attributes.as_ptr(), - ) - } else { - let visual_infos: *const ffi::XVisualInfo = visual_infos; - glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1) - }; - - (xlib.XSetErrorHandler)(old_callback); - - if context.is_null() { - // TODO: check for errors and return `OpenGlVersionNotSupported` - return Err(CreationError::OsError("GL context creation failed".to_string())); - } - - Ok(context) +impl DerefMut for Glx { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } -/// Enumerates all available FBConfigs -unsafe fn choose_fbconfig( - extensions: &str, - xconn: &Arc, - screen_id: raw::c_int, - pf_reqs: &PixelFormatRequirements, - surface_type: SurfaceType, - transparent: Option, -) -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat, ffi::XVisualInfo), CreationError> { - let glx = GLX.as_ref().unwrap(); - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - out.push(ffi::glx::X_RENDERABLE as raw::c_int); - out.push(1); - - if let Some(xid) = pf_reqs.x11_visual_xid { - // getting the visual infos - let fvi = crate::platform_impl::x11_utils::get_visual_info_from_xid(&xconn, xid); +impl Deref for GlxExtra { + type Target = glx_extra::Glx; - out.push(ffi::glx::X_VISUAL_TYPE as raw::c_int); - out.push(fvi.class as raw::c_int); - - out.push(ffi::glx::VISUAL_ID as raw::c_int); - out.push(xid as raw::c_int); - } else { - out.push(ffi::glx::X_VISUAL_TYPE as raw::c_int); - out.push(ffi::glx::TRUE_COLOR as raw::c_int); - } - - out.push(ffi::glx::DRAWABLE_TYPE as raw::c_int); - let surface_type = match surface_type { - SurfaceType::Window => ffi::glx::WINDOW_BIT, - SurfaceType::PBuffer => ffi::glx::PBUFFER_BIT, - SurfaceType::Surfaceless => ffi::glx::DONT_CARE, /* TODO: Properly support */ - }; - out.push(surface_type as raw::c_int); - - // TODO: Use RGB/RGB_FLOAT_BIT_ARB if they don't want alpha bits, - // fallback to it if they don't care - out.push(ffi::glx::RENDER_TYPE as raw::c_int); - if pf_reqs.float_color_buffer { - if check_ext(extensions, "GLX_ARB_fbconfig_float") { - out.push(ffi::glx_extra::RGBA_FLOAT_BIT_ARB as raw::c_int); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } else { - out.push(ffi::glx::RGBA_BIT as raw::c_int); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(ffi::glx::RED_SIZE as raw::c_int); - out.push((color / 3) as raw::c_int); - out.push(ffi::glx::GREEN_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int); - out.push(ffi::glx::BLUE_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(ffi::glx::ALPHA_SIZE as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(ffi::glx::DEPTH_SIZE as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(ffi::glx::STENCIL_SIZE as raw::c_int); - out.push(stencil as raw::c_int); - } - - let double_buffer = pf_reqs.double_buffer.unwrap_or(true); - out.push(ffi::glx::DOUBLEBUFFER as raw::c_int); - out.push(if double_buffer { 1 } else { 0 }); - - if let Some(multisampling) = pf_reqs.multisampling { - if check_ext(extensions, "GLX_ARB_multisample") { - out.push(ffi::glx_extra::SAMPLE_BUFFERS_ARB as raw::c_int); - out.push(if multisampling == 0 { 0 } else { 1 }); - out.push(ffi::glx_extra::SAMPLES_ARB as raw::c_int); - out.push(multisampling as raw::c_int); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } - - out.push(ffi::glx::STEREO as raw::c_int); - out.push(if pf_reqs.stereoscopy { 1 } else { 0 }); - - if pf_reqs.srgb { - if check_ext(extensions, "GLX_ARB_framebuffer_sRGB") { - out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int); - out.push(1); - } else if check_ext(extensions, "GLX_EXT_framebuffer_sRGB") { - out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int); - out.push(1); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - if check_ext(extensions, "GLX_ARB_context_flush_control") { - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as raw::c_int); - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as raw::c_int); - } - } - } - - out.push(ffi::glx::CONFIG_CAVEAT as raw::c_int); - out.push(ffi::glx::DONT_CARE as raw::c_int); - - out.push(0); - out - }; - - // calling glXChooseFBConfig - let (fb_config, visual_infos): (ffi::glx::types::GLXFBConfig, ffi::XVisualInfo) = { - let mut num_configs = 0; - let configs = glx.ChooseFBConfig( - xconn.display as *mut _, - screen_id, - descriptor.as_ptr(), - &mut num_configs, - ); - if configs.is_null() { - return Err(CreationError::NoAvailablePixelFormat); - } - if num_configs == 0 { - return Err(CreationError::NoAvailablePixelFormat); - } - - match crate::platform_impl::x11_utils::select_config( - xconn, - transparent, - pf_reqs, - (0..num_configs).collect(), - |config_id| { - let visual_infos_raw = glx.GetVisualFromFBConfig( - xconn.display as *mut _, - *configs.offset(*config_id as isize), - ); - - if visual_infos_raw.is_null() { - return None; - } - - let visual_infos: ffi::XVisualInfo = std::ptr::read(visual_infos_raw as *const _); - (xconn.xlib.XFree)(visual_infos_raw as *mut _); - Some(visual_infos) - }, - ) { - Ok((config_id, visual_infos)) => { - let config = *configs.offset(config_id as isize); - let config = config.clone(); - - (xconn.xlib.XFree)(configs as *mut _); - (config, visual_infos) - } - Err(()) => { - (xconn.xlib.XFree)(configs as *mut _); - return Err(CreationError::NoAvailablePixelFormat); - } - } - }; - - let get_attrib = |attrib: raw::c_int| -> i32 { - let mut value = 0; - glx.GetFBConfigAttrib(xconn.display as *mut _, fb_config, attrib, &mut value); - // TODO: check return value - value - }; - - let pf_desc = PixelFormat { - hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as raw::c_int) - != ffi::glx::SLOW_CONFIG as raw::c_int, - color_bits: get_attrib(ffi::glx::RED_SIZE as raw::c_int) as u8 - + get_attrib(ffi::glx::GREEN_SIZE as raw::c_int) as u8 - + get_attrib(ffi::glx::BLUE_SIZE as raw::c_int) as u8, - alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as raw::c_int) as u8, - depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as raw::c_int) as u8, - stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as raw::c_int) as u8, - stereoscopy: get_attrib(ffi::glx::STEREO as raw::c_int) != 0, - double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as raw::c_int) != 0, - multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as raw::c_int) != 0 { - Some(get_attrib(ffi::glx::SAMPLES as raw::c_int) as u16) - } else { - None - }, - srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int) != 0 - || get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int) != 0, - }; - - Ok((fb_config, pf_desc, visual_infos)) -} - -/// Checks if `ext` is available. -fn check_ext(extensions: &str, ext: &str) -> bool { - extensions.split(' ').find(|&s| s == ext).is_some() + fn deref(&self) -> &Self::Target { + &self.0 + } } -fn load_extensions( - xconn: &Arc, - screen_id: raw::c_int, -) -> Result { - unsafe { - let glx = GLX.as_ref().unwrap(); - let extensions = glx.QueryExtensionsString(xconn.display as *mut _, screen_id); - if extensions.is_null() { - return Err(CreationError::OsError( - "`glXQueryExtensionsString` found no glX extensions".to_string(), - )); - } - let extensions = CStr::from_ptr(extensions).to_bytes().to_vec(); - Ok(String::from_utf8(extensions).unwrap()) +impl DerefMut for GlxExtra { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs new file mode 100644 index 00000000000..ea558e17d8d --- /dev/null +++ b/glutin/src/api/glx/surface.rs @@ -0,0 +1,191 @@ +use std::marker::PhantomData; +use std::num::NonZeroU32; +use std::os::raw::{c_int, c_uint}; + +use raw_window_handle::RawWindowHandle; + +use glutin_glx_sys::glx; +use glutin_glx_sys::glx::types::GLXWindow; +use glutin_glx_sys::glx_extra; + +use crate::error::{ErrorKind, Result}; +use crate::surface::{ + AsRawSurface, DamageRect, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, + SurfaceAttributes, SurfaceType, SurfaceTypeTrait, WindowSurface, +}; + +use super::config::Config; +use super::display::Display; +use super::{Glx, GlxDisplay}; + +impl Display { + pub fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); + let xid = match native_pixmap { + NativePixmap::X11Pixmap(xid) => *xid, + _ => return Err(ErrorKind::NotSupported.into()), + }; + + let mut attrs = Vec::::new(); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + unsafe { + let surface = self.glx.CreatePixmap(self.display.0, config.config, xid, attrs.as_ptr()); + + super::last_glx_error(self.display)?; + + Ok(Surface { glx: self.glx, display: self.display, surface, _ty: PhantomData }) + } + } + + pub fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let width = surface_attributes.width.unwrap(); + let height = surface_attributes.height.unwrap(); + + let mut attrs = Vec::::new(); + + attrs.push(glx::PBUFFER_WIDTH as c_int); + attrs.push(width.get() as c_int); + attrs.push(glx::PBUFFER_HEIGHT as c_int); + attrs.push(height.get() as c_int); + attrs.push(glx::LARGEST_PBUFFER as c_int); + attrs.push(surface_attributes.largest_pbuffer as c_int); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + unsafe { + let surface = self.glx.CreatePbuffer(self.display.0, config.config, attrs.as_ptr()); + + super::last_glx_error(self.display)?; + + Ok(Surface { glx: self.glx, display: self.display, surface, _ty: PhantomData }) + } + } + + pub fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let window = match surface_attributes.raw_window_handle.unwrap() { + RawWindowHandle::Xlib(window_handle) => window_handle.window, + _ => return Err(ErrorKind::NotSupported.into()), + }; + + let mut attrs = Vec::::new(); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + unsafe { + let surface = self.glx.CreateWindow( + self.display.0, + config.config, + window, + attrs.as_ptr() as *const _, + ); + + super::last_glx_error(self.display)?; + + Ok(Surface { glx: self.glx, display: self.display, surface, _ty: PhantomData }) + } + } +} + +pub struct Surface { + glx: &'static Glx, + display: GlxDisplay, + pub(crate) surface: GLXWindow, + _ty: PhantomData, +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + RawSurface::Glx(self.surface as u64) + } +} + +impl Surface { + pub fn buffer_age(&self) -> u32 { + self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) as u32 + } + + pub fn width(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + pub fn height(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + pub fn is_single_buffered(&self) -> bool { + false + } + + pub fn swap_buffers(&self) -> Result<()> { + unsafe { + self.glx.SwapBuffers(self.display.0, self.surface); + super::last_glx_error(self.display) + } + } + + pub fn swap_buffers_with_damage(&self, _rects: &[DamageRect]) -> Result<()> { + Err(ErrorKind::NotSupported.into()) + } + + pub fn is_current(&self) -> bool { + self.is_current_draw() && self.is_current_read() + } + + pub fn is_current_draw(&self) -> bool { + unsafe { self.glx.GetCurrentDrawable() == self.surface } + } + + pub fn is_current_read(&self) -> bool { + unsafe { self.glx.GetCurrentReadDrawable() == self.surface } + } + + fn raw_attribute(&self, attr: c_int) -> c_uint { + unsafe { + let mut value = 0; + // This shouldn't generate any errors given that we know that the surface is valid. + self.glx.QueryDrawable(self.display.0, self.surface, attr, &mut value); + value + } + } +} + +impl Surface { + pub fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { + // This isn't supported with GLXDrawable. + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + match T::surface_type() { + SurfaceType::Pbuffer => { + self.glx.DestroyPbuffer(self.display.0, self.surface); + } + SurfaceType::Window => { + self.glx.DestroyWindow(self.display.0, self.surface); + } + SurfaceType::Pixmap => { + self.glx.DestroyPixmap(self.display.0, self.surface); + } + } + } + } +} diff --git a/glutin/src/api/ios/mod.rs b/glutin/src/api/ios/mod.rs deleted file mode 100644 index 4bf10ec85f9..00000000000 --- a/glutin/src/api/ios/mod.rs +++ /dev/null @@ -1,454 +0,0 @@ -#![cfg(target_os = "ios")] - -//! iOS support -//! -//! # Building app -//! To build ios app you will need rustc built for this targets: -//! -//! - armv7-apple-ios -//! - armv7s-apple-ios -//! - i386-apple-ios -//! - aarch64-apple-ios -//! - x86_64-apple-ios -//! -//! Then -//! -//! ``` -//! cargo build --target=... -//! ``` -//! The simplest way to integrate your app into xcode environment is to build it -//! as a static library. Wrap your main function and export it. -//! -//! ```rust, ignore -//! #[no_mangle] -//! pub extern fn start_glutin_app() { -//! start_inner() -//! } -//! -//! fn start_inner() { -//! ... -//! } -//! ``` -//! -//! Compile project and then drag resulting .a into Xcode project. Add glutin.h -//! to xcode. -//! -//! ```c -//! void start_glutin_app(); -//! ``` -//! -//! Use start_glutin_app inside your xcode's main function. -//! -//! -//! # App lifecycle and events -//! -//! iOS environment is very different from other platforms and you must be very -//! careful with it's events. Familiarize yourself with [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/). -//! -//! -//! This is how those event are represented in glutin: -//! -//! - applicationDidBecomeActive is Focused(true) -//! - applicationWillResignActive is Focused(false) -//! - applicationDidEnterBackground is Suspended(true) -//! - applicationWillEnterForeground is Suspended(false) -//! - applicationWillTerminate is Destroyed -//! -//! Keep in mind that after Destroyed event is received every attempt to draw -//! with opengl will result in segfault. -//! -//! Also note that app will not receive Destroyed event if suspended, it will be -//! SIGKILL'ed - -use crate::platform::ios::{WindowBuilderExtIOS, WindowExtIOS}; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, Rect, -}; - -use glutin_gles2_sys as ffi; -use objc::declare::ClassDecl; -use objc::runtime::{Class, Object, Sel, BOOL, NO, YES}; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::WindowBuilder; - -use std::ffi::CString; -use std::os::raw; - -#[derive(Debug, PartialEq)] -enum ColorFormat { - Rgba8888 = 0, - Rgb565 = 1, - Srgba8888 = 2, -} - -impl ColorFormat { - #[allow(non_upper_case_globals)] - pub fn for_view(view: ffi::id) -> Self { - let color_format: ffi::NSUInteger = unsafe { msg_send![view, drawableColorFormat] }; - match color_format { - ffi::GLKViewDrawableColorFormatRGBA8888 => ColorFormat::Rgba8888, - ffi::GLKViewDrawableColorFormatRGB565 => ColorFormat::Rgb565, - ffi::GLKViewDrawableColorFormatSRGBA8888 => ColorFormat::Srgba8888, - _ => unreachable!(), - } - } - - pub fn color_bits(&self) -> u8 { - if *self == ColorFormat::Rgba8888 || *self == ColorFormat::Srgba8888 { - 8 - } else { - 16 - } - } - - pub fn alpha_bits(&self) -> u8 { - if *self == ColorFormat::Rgba8888 || *self == ColorFormat::Srgba8888 { - 8 - } else { - 0 - } - } - - pub fn srgb(&self) -> bool { - *self == ColorFormat::Srgba8888 - } -} - -#[allow(non_upper_case_globals)] -fn depth_for_view(view: ffi::id) -> u8 { - let depth_format: ffi::NSUInteger = unsafe { msg_send![view, drawableDepthFormat] }; - match depth_format { - ffi::GLKViewDrawableDepthFormatNone => 0, - ffi::GLKViewDrawableDepthFormat16 => 16, - ffi::GLKViewDrawableDepthFormat24 => 24, - _ => unreachable!(), - } -} - -#[allow(non_upper_case_globals)] -fn stencil_for_view(view: ffi::id) -> u8 { - let stencil_format: ffi::NSUInteger = unsafe { msg_send![view, drawableStencilFormat] }; - match stencil_format { - ffi::GLKViewDrawableStencilFormatNone => 0, - ffi::GLKViewDrawableStencilFormat8 => 8, - _ => unreachable!(), - } -} - -#[allow(non_upper_case_globals)] -fn multisampling_for_view(view: ffi::id) -> Option { - let ms_format: ffi::NSUInteger = unsafe { msg_send![view, drawableMultisample] }; - match ms_format { - ffi::GLKViewDrawableMultisampleNone => None, - ffi::GLKViewDrawableMultisample4X => Some(4), - _ => unreachable!(), - } -} - -#[derive(Debug)] -pub struct Context { - eagl_context: ffi::id, - view: ffi::id, // this will be invalid after the `EventLoop` is dropped -} - -fn validate_version(version: u8) -> Result { - let version = version as ffi::NSUInteger; - if version >= ffi::kEAGLRenderingAPIOpenGLES1 && version <= ffi::kEAGLRenderingAPIOpenGLES3 { - Ok(version) - } else { - Err(CreationError::OsError(format!( - "Specified OpenGL ES version ({:?}) is not availble on iOS. Only 1, 2, and 3 are valid options", - version, - ))) - } -} - -impl Context { - #[inline] - pub fn new_windowed( - builder: WindowBuilder, - el: &EventLoopWindowTarget, - _: &PixelFormatRequirements, - gl_attrs: &GlAttributes<&Context>, - ) -> Result<(winit::window::Window, Self), CreationError> { - create_view_class(); - let view_class = Class::get("MainGLView").expect("Failed to get class `MainGLView`"); - let builder = builder.with_root_view_class(view_class as *const _ as *const _); - if gl_attrs.sharing.is_some() { - unimplemented!("Shared contexts are unimplemented on iOS."); - } - let version = match gl_attrs.version { - GlRequest::Latest => ffi::kEAGLRenderingAPIOpenGLES3, - GlRequest::Specific(api, (major, _minor)) => { - if api == Api::OpenGlEs { - validate_version(major)? - } else { - return Err(CreationError::OsError(format!( - "Specified API ({:?}) is not availble on iOS. Only `Api::OpenGlEs` can be used", - api, - ))); - } - } - GlRequest::GlThenGles { opengles_version: (major, _minor), .. } => { - validate_version(major)? - } - }; - let win = builder.build(el)?; - let context = unsafe { - let eagl_context = Context::create_context(version)?; - let view = win.ui_view() as ffi::id; - let mut context = Context { eagl_context, view }; - context.init_context(&win); - context - }; - Ok((win, context)) - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - let wb = winit::window::WindowBuilder::new().with_visible(false).with_inner_size(size); - Self::new_windowed(wb, el, pf_reqs, gl_attr).map(|(_window, context)| context) - } - - unsafe fn create_context(mut version: ffi::NSUInteger) -> Result { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let eagl_context: ffi::id = msg_send![context_class, alloc]; - let mut valid_context = ffi::nil; - while valid_context == ffi::nil && version > 0 { - valid_context = msg_send![eagl_context, initWithAPI: version]; - version -= 1; - } - if valid_context == ffi::nil { - Err(CreationError::OsError( - "Failed to create an OpenGL ES context with any version".to_string(), - )) - } else { - Ok(eagl_context) - } - } - - unsafe fn init_context(&mut self, win: &winit::window::Window) { - let dict_class = Class::get("NSDictionary").expect("Failed to get class `NSDictionary`"); - let number_class = Class::get("NSNumber").expect("Failed to get class `NSNumber`"); - let draw_props: ffi::id = msg_send![dict_class, alloc]; - let draw_props: ffi::id = msg_send![draw_props, - initWithObjects: - vec![ - msg_send![number_class, numberWithBool:NO], - ffi::kEAGLColorFormatRGB565, - ].as_ptr() - forKeys: - vec![ - ffi::kEAGLDrawablePropertyRetainedBacking, - ffi::kEAGLDrawablePropertyColorFormat, - ].as_ptr() - count: 2 - ]; - self.make_current().unwrap(); - - let view = self.view; - let scale_factor = win.scale_factor() as ffi::CGFloat; - let _: () = msg_send![view, setContentScaleFactor: scale_factor]; - let layer: ffi::id = msg_send![view, layer]; - let _: () = msg_send![layer, setContentsScale: scale_factor]; - let _: () = msg_send![layer, setDrawableProperties: draw_props]; - - let gl = ffi::gles::Gles2::load_with(|symbol| { - self.get_proc_address(symbol) as *const raw::c_void - }); - let mut color_render_buf: ffi::gles::types::GLuint = 0; - let mut frame_buf: ffi::gles::types::GLuint = 0; - gl.GenRenderbuffers(1, &mut color_render_buf); - gl.BindRenderbuffer(ffi::gles::RENDERBUFFER, color_render_buf); - - let ok: BOOL = msg_send![self.eagl_context, renderbufferStorage:ffi::gles::RENDERBUFFER fromDrawable:layer]; - if ok != YES { - panic!("EAGL: could not set renderbufferStorage"); - } - - gl.GenFramebuffers(1, &mut frame_buf); - gl.BindFramebuffer(ffi::gles::FRAMEBUFFER, frame_buf); - - gl.FramebufferRenderbuffer( - ffi::gles::FRAMEBUFFER, - ffi::gles::COLOR_ATTACHMENT0, - ffi::gles::RENDERBUFFER, - color_render_buf, - ); - - let status = gl.CheckFramebufferStatus(ffi::gles::FRAMEBUFFER); - if gl.CheckFramebufferStatus(ffi::gles::FRAMEBUFFER) != ffi::gles::FRAMEBUFFER_COMPLETE { - panic!("framebuffer status: {:?}", status); - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - unsafe { - let res: BOOL = - msg_send![self.eagl_context, presentRenderbuffer: ffi::gles::RENDERBUFFER]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext presentRenderbuffer` failed", - ))) - } - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - 0 - } - - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - let color_format = ColorFormat::for_view(self.view); - PixelFormat { - hardware_accelerated: true, - color_bits: color_format.color_bits(), - alpha_bits: color_format.alpha_bits(), - depth_bits: depth_for_view(self.view), - stencil_bits: stencil_for_view(self.view), - stereoscopy: false, - double_buffer: true, - multisampling: multisampling_for_view(self.view), - srgb: color_format.srgb(), - } - } - - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // N/A - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let res: BOOL = msg_send![context_class, setCurrentContext: self.eagl_context]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext setCurrentContext` failed", - ))) - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if !self.is_current() { - return Ok(()); - } - - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let res: BOOL = msg_send![context_class, setCurrentContext: ffi::nil]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext setCurrentContext` failed", - ))) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - // TODO: This can likely be implemented using - // `currentContext`/`getCurrentContext` - true - } - - #[inline] - pub fn get_proc_address(&self, proc_name: &str) -> *const core::ffi::c_void { - let proc_name_c = CString::new(proc_name).expect("proc name contained interior nul byte"); - let path = b"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\0"; - let addr = unsafe { - let lib = - ffi::dlopen(path.as_ptr() as *const raw::c_char, ffi::RTLD_LAZY | ffi::RTLD_GLOBAL); - ffi::dlsym(lib, proc_name_c.as_ptr()) as *const _ - }; - // debug!("proc {} -> {:?}", proc_name, addr); - addr - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - self.eagl_context as *mut raw::c_void - } - - #[inline] - pub fn get_api(&self) -> Api { - Api::OpenGlEs - } -} - -fn create_view_class() { - extern "C" fn init_with_frame(this: &Object, _: Sel, frame: ffi::CGRect) -> ffi::id { - unsafe { - let view: ffi::id = msg_send![super(this, class!(GLKView)), initWithFrame: frame]; - - let mask = ffi::UIViewAutoresizingFlexibleWidth | ffi::UIViewAutoresizingFlexibleHeight; - let _: () = msg_send![view, setAutoresizingMask: mask]; - let _: () = msg_send![view, setAutoresizesSubviews: YES]; - - let layer: ffi::id = msg_send![view, layer]; - let _: () = msg_send![layer, setOpaque: YES]; - - view - } - } - - extern "C" fn layer_class(_: &Class, _: Sel) -> *const Class { - unsafe { - std::mem::transmute( - Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`"), - ) - } - } - - let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`"); - let mut decl = - ClassDecl::new("MainGLView", superclass).expect("Failed to declare class `MainGLView`"); - unsafe { - decl.add_method( - sel!(initWithFrame:), - init_with_frame as extern "C" fn(&Object, Sel, ffi::CGRect) -> ffi::id, - ); - decl.add_class_method( - sel!(layerClass), - layer_class as extern "C" fn(&Class, Sel) -> *const Class, - ); - decl.register(); - } -} - -impl Drop for Context { - fn drop(&mut self) { - let _: () = unsafe { msg_send![self.eagl_context, release] }; - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} diff --git a/glutin/src/api/mod.rs b/glutin/src/api/mod.rs index 8f438ccd1fe..842563808eb 100644 --- a/glutin/src/api/mod.rs +++ b/glutin/src/api/mod.rs @@ -1,6 +1,6 @@ -pub mod dlloader; +#[cfg(feature = "egl")] pub mod egl; +#[cfg(feature = "glx")] pub mod glx; -pub mod ios; -pub mod osmesa; +#[cfg(windows)] pub mod wgl; diff --git a/glutin/src/api/osmesa/mod.rs b/glutin/src/api/osmesa/mod.rs deleted file mode 100644 index 89c07658ebf..00000000000 --- a/glutin/src/api/osmesa/mod.rs +++ /dev/null @@ -1,224 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -pub mod ffi { - pub use osmesa_sys::OSMesaContext; -} - -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormatRequirements, - Robustness, -}; - -use winit::dpi; - -use std::ffi::CString; -use std::os::raw; - -#[derive(Debug)] -pub struct OsMesaContext { - context: osmesa_sys::OSMesaContext, - buffer: Vec, - width: u32, - height: u32, -} - -#[derive(Debug)] -struct NoEsOrWebGlSupported; - -impl std::fmt::Display for NoEsOrWebGlSupported { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "OsMesa only works with desktop OpenGL; OpenGL ES or WebGL are not supported") - } -} - -impl std::error::Error for NoEsOrWebGlSupported { - fn description(&self) -> &str { - "OsMesa only works with desktop OpenGL" - } -} - -#[derive(Debug)] -struct LoadingError(String); - -impl LoadingError { - fn new(d: D) -> Self { - LoadingError(format!("{:?}", d)) - } -} - -impl std::fmt::Display for LoadingError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "Failed to load OsMesa dynamic library: {}", self.0) - } -} - -impl std::error::Error for LoadingError { - fn description(&self) -> &str { - "The library or a symbol of it could not be loaded" - } -} - -impl OsMesaContext { - pub fn new( - _pf_reqs: &PixelFormatRequirements, - opengl: &GlAttributes<&OsMesaContext>, - size: dpi::PhysicalSize, - ) -> Result { - osmesa_sys::OsMesa::try_loading() - .map_err(LoadingError::new) - .map_err(|e| CreationError::NoBackendAvailable(Box::new(e)))?; - - if opengl.sharing.is_some() { - panic!("Context sharing not possible with OsMesa") - } - - match opengl.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported.into()); - } - _ => (), - } - - // TODO: use `pf_reqs` for the format - - let mut attribs = Vec::new(); - - if let Some(profile) = opengl.profile { - attribs.push(osmesa_sys::OSMESA_PROFILE); - - match profile { - GlProfile::Compatibility => { - attribs.push(osmesa_sys::OSMESA_COMPAT_PROFILE); - } - GlProfile::Core => { - attribs.push(osmesa_sys::OSMESA_CORE_PROFILE); - } - } - } - - match opengl.version { - GlRequest::Latest => {} - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); - attribs.push(major as raw::c_int); - attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); - attribs.push(minor as raw::c_int); - } - GlRequest::Specific(Api::OpenGlEs, _) | GlRequest::Specific(Api::WebGl, _) => { - return Err(CreationError::NoBackendAvailable(Box::new(NoEsOrWebGlSupported))); - } - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); - attribs.push(major as raw::c_int); - attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); - attribs.push(minor as raw::c_int); - } - } - - // attribs array must be NULL terminated. - attribs.push(0); - - let size: (u32, u32) = size.into(); - - Ok(OsMesaContext { - width: size.0, - height: size.1, - buffer: std::iter::repeat(unsafe { std::mem::zeroed() }) - .take((size.0 * size.1) as usize) - .collect(), - context: unsafe { - let ctx = - osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), std::ptr::null_mut()); - if ctx.is_null() { - return Err(CreationError::OsError( - "OSMesaCreateContextAttribs failed".to_string(), - )); - } - ctx - }, - }) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let ret = osmesa_sys::OSMesaMakeCurrent( - self.context, - self.buffer.as_ptr() as *mut _, - 0x1401, - self.width as raw::c_int, - self.height as raw::c_int, - ); - - // an error can only happen in case of invalid parameter, which would - // indicate a bug in glutin - if ret == 0 { - panic!("OSMesaMakeCurrent failed"); - } - - Ok(()) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if osmesa_sys::OSMesaGetCurrentContext() == self.context { - // Supported with the non-gallium drivers, but not the gallium ones - // I (gentz) have filed a patch upstream to mesa to correct this, - // however, older users (or anyone not running mesa-git, tbh) - // probably won't support this. - // - // There is no way to tell, ofc, without just calling the function - // and seeing if it work. - // - // https://gitlab.freedesktop.org/mesa/mesa/merge_requests/533 - let ret = - osmesa_sys::OSMesaMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut(), 0, 0, 0); - - if ret == 0 { - unimplemented!( - "OSMesaMakeCurrent failed to make the context not current. This most likely means that you're using an older gallium-based mesa driver." - ) - } - } - - Ok(()) - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context } - } - - #[inline] - pub fn get_api(&self) -> Api { - Api::OpenGl - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - self.context as *mut _ - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - unsafe { - let c_str = CString::new(addr.as_bytes().to_vec()).unwrap(); - core::mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr() as *mut _)) - } - } -} - -impl Drop for OsMesaContext { - #[inline] - fn drop(&mut self) { - unsafe { osmesa_sys::OSMesaDestroyContext(self.context) } - } -} - -unsafe impl Send for OsMesaContext {} -unsafe impl Sync for OsMesaContext {} diff --git a/glutin/src/api/wgl/make_current_guard.rs b/glutin/src/api/wgl/make_current_guard.rs deleted file mode 100644 index c343ec2c676..00000000000 --- a/glutin/src/api/wgl/make_current_guard.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::gl; -use crate::CreationError; - -use winapi::shared::windef::{HDC, HGLRC}; - -use std::io; -use std::marker::PhantomData; -use std::os::raw; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct CurrentContextGuard<'a, 'b> { - previous_hdc: HDC, - previous_hglrc: HGLRC, - marker1: PhantomData<&'a ()>, - marker2: PhantomData<&'b ()>, -} - -impl<'a, 'b> CurrentContextGuard<'a, 'b> { - pub unsafe fn make_current( - hdc: HDC, - context: HGLRC, - ) -> Result, CreationError> { - let previous_hdc = gl::wgl::GetCurrentDC() as HDC; - let previous_hglrc = gl::wgl::GetCurrentContext() as HGLRC; - - let result = gl::wgl::MakeCurrent(hdc as *const _, context as *const _); - if result == 0 { - return Err(CreationError::OsError(format!( - "wglMakeCurrent function failed: {}", - io::Error::last_os_error() - ))); - } - - Ok(CurrentContextGuard { - previous_hdc, - previous_hglrc, - marker1: PhantomData, - marker2: PhantomData, - }) - } -} - -impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { - fn drop(&mut self) { - unsafe { - gl::wgl::MakeCurrent( - self.previous_hdc as *const raw::c_void, - self.previous_hglrc as *const raw::c_void, - ); - } - } -} diff --git a/glutin/src/api/wgl/mod.rs b/glutin/src/api/wgl/mod.rs index 7cd633cdf2f..932e6fc3f5e 100644 --- a/glutin/src/api/wgl/mod.rs +++ b/glutin/src/api/wgl/mod.rs @@ -1,912 +1,28 @@ -#![cfg(any(target_os = "windows"))] +use std::marker::PhantomData; -mod make_current_guard; +use crate::config::ConfigTemplate; +use crate::surface::SurfaceTypeTrait; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; - -use self::make_current_guard::CurrentContextGuard; - -use glutin_wgl_sys as gl; -use winapi::shared::minwindef::HMODULE; -use winapi::shared::minwindef::*; -use winapi::shared::ntdef::LPCWSTR; -use winapi::shared::windef::{HDC, HGLRC, HWND}; -use winapi::um::libloaderapi::*; -use winapi::um::wingdi::*; -use winapi::um::winuser::*; - -use std::ffi::{CStr, CString, OsStr}; -use std::os::raw; -use std::os::windows::ffi::OsStrExt; - -/// A WGL context. -/// -/// Note: should be destroyed before its window. -#[derive(Debug)] -pub struct Context { - context: ContextWrapper, - - hdc: HDC, - - /// Bound to `opengl32.dll`. - /// - /// `wglGetProcAddress` returns null for GL 1.1 functions because they are - /// already defined by the system. This module contains them. - gl_library: HMODULE, - - /// The pixel format that has been used to create this context. - pixel_format: PixelFormat, -} - -/// A simple wrapper that destroys the window when it is destroyed. #[derive(Debug)] -struct WindowWrapper(HWND, HDC); - -impl Drop for WindowWrapper { - #[inline] - fn drop(&mut self) { - unsafe { - DestroyWindow(self.0); - } +pub struct Display; +impl Display { + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + Some(Box::new(Vec::new().into_iter())) } } -/// Wraps around a context so that it is destroyed when necessary. #[derive(Debug)] -struct ContextWrapper(HGLRC); - -impl Drop for ContextWrapper { - #[inline] - fn drop(&mut self) { - unsafe { - gl::wgl::DeleteContext(self.0 as *const _); - } - } +pub struct Surface { + _ty: PhantomData, } -impl Context { - /// Attempt to build a new WGL context on a window. - /// - /// # Unsafety - /// - /// The `window` must continue to exist as long as the resulting `Context` - /// exists. - #[inline] - pub unsafe fn new( - pf_reqs: &PixelFormatRequirements, - opengl: &GlAttributes, - win: HWND, - ) -> Result { - let hdc = GetDC(win); - if hdc.is_null() { - let err = Err(CreationError::OsError(format!( - "GetDC function failed: {}", - std::io::Error::last_os_error() - ))); - return err; - } - - // loading the functions that are not guaranteed to be supported - let extra_functions = load_extra_functions(win)?; - - // getting the list of the supported extensions - let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() { - let data = extra_functions.GetExtensionsStringARB(hdc as *const _); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - } else if extra_functions.GetExtensionsStringEXT.is_loaded() { - let data = extra_functions.GetExtensionsStringEXT(); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - } else { - format!("") - }; - - let use_arb_for_pixel_format = - extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some(); - - // calling SetPixelFormat, if not already done - let mut pixel_format_id = GetPixelFormat(hdc); - if pixel_format_id == 0 { - let id = if use_arb_for_pixel_format { - choose_arb_pixel_format_id(&extra_functions, &extensions, hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - } else { - choose_native_pixel_format_id(hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - }; - - set_pixel_format(hdc, id)?; - pixel_format_id = id; - } - - let pixel_format = if use_arb_for_pixel_format { - choose_arb_pixel_format(&extra_functions, &extensions, hdc, pixel_format_id) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - } else { - choose_native_pixel_format(hdc, pf_reqs, pixel_format_id) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - }; - - // creating the OpenGL context - let context = - create_context(Some((&extra_functions, pf_reqs, opengl, &extensions)), win, hdc)?; - - // loading the opengl32 module - let gl_library = load_opengl32_dll()?; - - // handling vsync - if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { - let _guard = CurrentContextGuard::make_current(hdc, context.0)?; - - if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) == 0 { - return Err(CreationError::OsError("wglSwapIntervalEXT failed".to_string())); - } - } - - Ok(Context { context, hdc, gl_library, pixel_format }) - } - - /// Returns the raw HGLRC. - #[inline] - pub fn get_hglrc(&self) -> HGLRC { - self.context.0 - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::last_os_error())) - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if self.is_current() && gl::wgl::MakeCurrent(self.hdc as *const _, std::ptr::null()) != 0 { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::last_os_error())) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const raw::c_void } - } - - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - - unsafe { - let p = gl::wgl::GetProcAddress(addr) as *const core::ffi::c_void; - if !p.is_null() { - return p; - } - GetProcAddress(self.gl_library, addr) as *const _ - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - // TODO: decide how to handle the error - // if unsafe { SwapBuffers(self.hdc) } != 0 { - // Ok(()) - // } else { - // Err(ContextError::IoError(std::io::Error::last_os_error())) - // } - unsafe { SwapBuffers(self.hdc) }; - Ok(()) - } - - #[inline] - pub fn get_api(&self) -> Api { - // FIXME: can be opengl es - Api::OpenGl - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -/// Creates an OpenGL context. -/// -/// If `extra` is `Some`, this function will attempt to use the latest WGL -/// functions to create the context. -/// -/// Otherwise, only the basic API will be used and the chances of -/// `CreationError::NotSupported` being returned increase. -unsafe fn create_context( - extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequirements, &GlAttributes, &str)>, - _: HWND, - hdc: HDC, -) -> Result { - let share; - - if let Some((extra_functions, _pf_reqs, opengl, extensions)) = extra { - share = opengl.sharing.unwrap_or(std::ptr::null_mut()); - - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_some() { - let mut attributes = Vec::new(); - - match opengl.version { - GlRequest::Latest => {} - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - GlRequest::Specific(Api::OpenGlEs, (major, minor)) => { - if extensions - .split(' ') - .find(|&i| i == "WGL_EXT_create_context_es2_profile") - .is_some() - { - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as raw::c_int); - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - GlRequest::Specific(_, _) => { - return Err(CreationError::OpenGlVersionNotSupported); - } - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - } - - if let Some(profile) = opengl.profile { - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_profile").is_some() - { - let flag = match profile { - GlProfile::Compatibility => { - gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB - } - GlProfile::Core => gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, - }; - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(flag as raw::c_int); - } else { - return Err(CreationError::NotSupported( - "required extension \"WGL_ARB_create_context_profile\" not found" - .to_string(), - )); - } - } - - let flags = { - let mut flags = 0; - - // robustness - if extensions - .split(' ') - .find(|&i| i == "WGL_ARB_create_context_robustness") - .is_some() - { - match opengl.robustness { - Robustness::RobustNoResetNotification - | Robustness::TryRobustNoResetNotification => { - attributes.push( - gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as raw::c_int); - flags = - flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::RobustLoseContextOnReset - | Robustness::TryRobustLoseContextOnReset => { - attributes.push( - gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as raw::c_int); - flags = - flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::NotRobust => (), - Robustness::NoError => (), - } - } else { - match opengl.robustness { - Robustness::RobustNoResetNotification - | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - } - - if opengl.debug { - flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int; - } - - flags - }; - - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as raw::c_int); - attributes.push(flags); - - attributes.push(0); - - let ctx = extra_functions.CreateContextAttribsARB( - hdc as *const raw::c_void, - share as *const raw::c_void, - attributes.as_ptr(), - ); - - if ctx.is_null() { - return Err(CreationError::OsError(format!( - "wglCreateContextAttribsARB failed: {}", - std::io::Error::last_os_error() - ))); - } else { - return Ok(ContextWrapper(ctx as HGLRC)); - } - } - } else { - share = std::ptr::null_mut(); - } - - let ctx = gl::wgl::CreateContext(hdc as *const raw::c_void); - if ctx.is_null() { - return Err(CreationError::OsError(format!( - "wglCreateContext failed: {}", - std::io::Error::last_os_error() - ))); - } - - if !share.is_null() { - if gl::wgl::ShareLists(share as *const raw::c_void, ctx) == 0 { - return Err(CreationError::OsError(format!( - "wglShareLists failed: {}", - std::io::Error::last_os_error() - ))); - } - }; - - Ok(ContextWrapper(ctx as HGLRC)) -} - -/// Chooses a pixel formats without using WGL. -/// -/// Gives less precise results than `enumerate_arb_pixel_formats`. -unsafe fn choose_native_pixel_format_id( - hdc: HDC, - pf_reqs: &PixelFormatRequirements, -) -> Result { - // TODO: hardware acceleration is not handled - - // handling non-supported stuff - if pf_reqs.float_color_buffer { - return Err(()); - } - - match pf_reqs.multisampling { - Some(0) => (), - None => (), - Some(_) => return Err(()), - }; - - if pf_reqs.stereoscopy { - return Err(()); - } - - if pf_reqs.srgb { - return Err(()); - } - - if pf_reqs.release_behavior != ReleaseBehavior::Flush { - return Err(()); - } - - // building the descriptor to pass to ChoosePixelFormat - let descriptor = PIXELFORMATDESCRIPTOR { - nSize: std::mem::size_of::() as u16, - nVersion: 1, - dwFlags: { - let f1 = match pf_reqs.double_buffer { - None => PFD_DOUBLEBUFFER, /* Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose */ - Some(true) => PFD_DOUBLEBUFFER, - Some(false) => 0, - }; - - let f2 = if pf_reqs.stereoscopy { PFD_STEREO } else { 0 }; - - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | f1 | f2 - }, - iPixelType: PFD_TYPE_RGBA, - cColorBits: pf_reqs.color_bits.unwrap_or(0), - cRedBits: 0, - cRedShift: 0, - cGreenBits: 0, - cGreenShift: 0, - cBlueBits: 0, - cBlueShift: 0, - cAlphaBits: pf_reqs.alpha_bits.unwrap_or(0), - cAlphaShift: 0, - cAccumBits: 0, - cAccumRedBits: 0, - cAccumGreenBits: 0, - cAccumBlueBits: 0, - cAccumAlphaBits: 0, - cDepthBits: pf_reqs.depth_bits.unwrap_or(0), - cStencilBits: pf_reqs.stencil_bits.unwrap_or(0), - cAuxBuffers: 0, - iLayerType: PFD_MAIN_PLANE, - bReserved: 0, - dwLayerMask: 0, - dwVisibleMask: 0, - dwDamageMask: 0, - }; - - // now querying - let pf_id = ChoosePixelFormat(hdc, &descriptor); - if pf_id == 0 { - return Err(()); - } - - Ok(pf_id) -} - -unsafe fn choose_native_pixel_format( - hdc: HDC, - pf_reqs: &PixelFormatRequirements, - pf_id: raw::c_int, -) -> Result { - // querying back the capabilities of what windows told us - let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed(); - if DescribePixelFormat( - hdc, - pf_id, - std::mem::size_of::() as u32, - &mut output, - ) == 0 - { - return Err(()); - } - - // windows may return us a non-conforming pixel format if none are - // supported, so we have to check this - if (output.dwFlags & PFD_DRAW_TO_WINDOW) == 0 { - return Err(()); - } - if (output.dwFlags & PFD_SUPPORT_OPENGL) == 0 { - return Err(()); - } - if output.iPixelType != PFD_TYPE_RGBA { - return Err(()); - } - - let pf_desc = PixelFormat { - hardware_accelerated: (output.dwFlags & PFD_GENERIC_FORMAT) == 0, - color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits, - alpha_bits: output.cAlphaBits, - depth_bits: output.cDepthBits, - stencil_bits: output.cStencilBits, - stereoscopy: (output.dwFlags & PFD_STEREO) != 0, - double_buffer: (output.dwFlags & PFD_DOUBLEBUFFER) != 0, - multisampling: None, - srgb: false, - }; - - if pf_desc.alpha_bits < pf_reqs.alpha_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.depth_bits < pf_reqs.depth_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.stencil_bits < pf_reqs.stencil_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.color_bits < pf_reqs.color_bits.unwrap_or(0) { - return Err(()); - } - if let Some(req) = pf_reqs.hardware_accelerated { - if pf_desc.hardware_accelerated != req { - return Err(()); - } - } - if let Some(req) = pf_reqs.double_buffer { - if pf_desc.double_buffer != req { - return Err(()); - } - } - - Ok(pf_desc) -} - -/// Enumerates the list of pixel formats by using extra WGL functions. -/// -/// Gives more precise results than `enumerate_native_pixel_formats`. -unsafe fn choose_arb_pixel_format_id( - extra: &gl::wgl_extra::Wgl, - extensions: &str, - hdc: HDC, - pf_reqs: &PixelFormatRequirements, -) -> Result { - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as raw::c_int); - out.push(1); - - out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as raw::c_int); - out.push(1); - - out.push(gl::wgl_extra::PIXEL_TYPE_ARB as raw::c_int); - if pf_reqs.float_color_buffer { - if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format_float").is_some() { - out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as raw::c_int); - } else { - return Err(()); - } - } else { - out.push(gl::wgl_extra::TYPE_RGBA_ARB as raw::c_int); - } - - if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated { - out.push(gl::wgl_extra::ACCELERATION_ARB as raw::c_int); - out.push(if hardware_accelerated { - gl::wgl_extra::FULL_ACCELERATION_ARB as raw::c_int - } else { - gl::wgl_extra::NO_ACCELERATION_ARB as raw::c_int - }); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(gl::wgl_extra::COLOR_BITS_ARB as raw::c_int); - out.push(color as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(gl::wgl_extra::ALPHA_BITS_ARB as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(gl::wgl_extra::DEPTH_BITS_ARB as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(gl::wgl_extra::STENCIL_BITS_ARB as raw::c_int); - out.push(stencil as raw::c_int); - } - - // Prefer double buffering if unspecified (probably shouldn't once you - // can choose) - let double_buffer = pf_reqs.double_buffer.unwrap_or(true); - out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as raw::c_int); - out.push(if double_buffer { 1 } else { 0 }); - - if let Some(multisampling) = pf_reqs.multisampling { - if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { - out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as raw::c_int); - out.push(if multisampling == 0 { 0 } else { 1 }); - out.push(gl::wgl_extra::SAMPLES_ARB as raw::c_int); - out.push(multisampling as raw::c_int); - } else { - return Err(()); - } - } - - out.push(gl::wgl_extra::STEREO_ARB as raw::c_int); - out.push(if pf_reqs.stereoscopy { 1 } else { 0 }); - - // WGL_*_FRAMEBUFFER_SRGB might be assumed to be true if not listed; - // so it's best to list it out and set its value as necessary. - if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { - out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int); - out.push(pf_reqs.srgb as raw::c_int); - } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { - out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int); - out.push(pf_reqs.srgb as raw::c_int); - } else if pf_reqs.srgb { - return Err(()); - } - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - if extensions.split(' ').find(|&i| i == "WGL_ARB_context_flush_control").is_some() { - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as raw::c_int); - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as raw::c_int); - } - } - } - - out.push(0); - out - }; - - let mut format_id = std::mem::zeroed(); - let mut num_formats = std::mem::zeroed(); - if extra.ChoosePixelFormatARB( - hdc as *const _, - descriptor.as_ptr(), - std::ptr::null(), - 1, - &mut format_id, - &mut num_formats, - ) == 0 - { - return Err(()); - } - - if num_formats == 0 { - return Err(()); - } - - Ok(format_id) -} - -unsafe fn choose_arb_pixel_format( - extra: &gl::wgl_extra::Wgl, - extensions: &str, - hdc: HDC, - format_id: raw::c_int, -) -> Result { - let get_info = |attrib: u32| { - let mut value = std::mem::zeroed(); - extra.GetPixelFormatAttribivARB( - hdc as *const _, - format_id as raw::c_int, - 0, - 1, - [attrib as raw::c_int].as_ptr(), - &mut value, - ); - value as u32 - }; - - let pf_desc = PixelFormat { - hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) - != gl::wgl_extra::NO_ACCELERATION_ARB, - color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 - + get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 - + get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8, - alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8, - depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8, - stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8, - stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0, - double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0, - multisampling: { - if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { - match get_info(gl::wgl_extra::SAMPLES_ARB) { - 0 => None, - a => Some(a as u16), - } - } else { - None - } - }, - srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { - get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 - } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { - get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 - } else { - false - }, - }; - - Ok(pf_desc) -} - -/// Calls `SetPixelFormat` on a window. -unsafe fn set_pixel_format(hdc: HDC, id: raw::c_int) -> Result<(), CreationError> { - let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed(); - - if DescribePixelFormat( - hdc, - id, - std::mem::size_of::() as UINT, - &mut output, - ) == 0 - { - return Err(CreationError::OsError(format!( - "DescribePixelFormat function failed: {}", - std::io::Error::last_os_error() - ))); - } - - if SetPixelFormat(hdc, id, &output) == 0 { - return Err(CreationError::OsError(format!( - "SetPixelFormat function failed: {}", - std::io::Error::last_os_error() - ))); - } - - Ok(()) -} - -/// Loads the `opengl32.dll` library. -unsafe fn load_opengl32_dll() -> Result { - let name = - OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()).collect::>(); - - let lib = LoadLibraryW(name.as_ptr()); - - if lib.is_null() { - return Err(CreationError::OsError(format!( - "LoadLibrary function failed: {}", - std::io::Error::last_os_error() - ))); - } - - Ok(lib) -} - -/// Loads the WGL functions that are not guaranteed to be supported. -/// -/// The `window` must be passed because the driver can vary depending on the -/// window's characteristics. -unsafe fn load_extra_functions(win: HWND) -> Result { - let (ex_style, style) = (WS_EX_APPWINDOW, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); - - // creating a dummy invisible window - let dummy_win = { - // getting the rect of the real window - let rect = { - let mut placement: WINDOWPLACEMENT = std::mem::zeroed(); - placement.length = std::mem::size_of::() as UINT; - if GetWindowPlacement(win, &mut placement) == 0 { - panic!(); - } - placement.rcNormalPosition - }; - - // getting the class name of the real window - let mut class_name = [0u16; 128]; - if GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { - return Err(CreationError::OsError(format!( - "GetClassNameW function failed: {}", - std::io::Error::last_os_error() - ))); - } - - // access to class information of the real window - let instance = GetModuleHandleW(std::ptr::null()); - let mut class: WNDCLASSEXW = std::mem::zeroed(); - - if GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 { - return Err(CreationError::OsError(format!( - "GetClassInfoExW function failed: {}", - std::io::Error::last_os_error() - ))); - } - - // register a new class for the dummy window, - // similar to the class of the real window but with a different callback - let class_name = OsStr::new("WglDummy Class") - .encode_wide() - .chain(Some(0).into_iter()) - .collect::>(); - - class.cbSize = std::mem::size_of::() as UINT; - class.lpszClassName = class_name.as_ptr(); - class.lpfnWndProc = Some(DefWindowProcW); - - // this shouldn't fail if the registration of the real window class - // worked. multiple registrations of the window class trigger an - // error which we want to ignore silently (e.g for multi-window - // setups) - RegisterClassExW(&class); - - // this dummy window should match the real one enough to get the same - // OpenGL driver - let title = - OsStr::new("dummy window").encode_wide().chain(Some(0).into_iter()).collect::>(); - let win = CreateWindowExW( - ex_style, - class_name.as_ptr(), - title.as_ptr() as LPCWSTR, - style, - CW_USEDEFAULT, - CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - std::ptr::null_mut(), - std::ptr::null_mut(), - GetModuleHandleW(std::ptr::null()), - std::ptr::null_mut(), - ); - - if win.is_null() { - return Err(CreationError::OsError(format!( - "CreateWindowEx function failed: {}", - std::io::Error::last_os_error() - ))); - } - - let hdc = GetDC(win); - if hdc.is_null() { - let err = Err(CreationError::OsError(format!( - "GetDC function failed: {}", - std::io::Error::last_os_error() - ))); - return err; - } - - WindowWrapper(win, hdc) - }; - - // getting the pixel format that we will use and setting it - { - let id = choose_dummy_pixel_format(dummy_win.1)?; - set_pixel_format(dummy_win.1, id)?; - } - - // creating the dummy OpenGL context and making it current - let dummy_ctx = create_context(None, dummy_win.0, dummy_win.1)?; - let _current_context = CurrentContextGuard::make_current(dummy_win.1, dummy_ctx.0)?; - - // loading the extra WGL functions - Ok(gl::wgl_extra::Wgl::load_with(|addr| { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - gl::wgl::GetProcAddress(addr) as *const raw::c_void - })) -} - -/// This function chooses a pixel format that is likely to be provided by -/// the main video driver of the system. -fn choose_dummy_pixel_format(hdc: HDC) -> Result { - // building the descriptor to pass to ChoosePixelFormat - let descriptor = PIXELFORMATDESCRIPTOR { - nSize: std::mem::size_of::() as u16, - nVersion: 1, - dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - iPixelType: PFD_TYPE_RGBA, - cColorBits: 24, - cRedBits: 0, - cRedShift: 0, - cGreenBits: 0, - cGreenShift: 0, - cBlueBits: 0, - cBlueShift: 0, - cAlphaBits: 8, - cAlphaShift: 0, - cAccumBits: 0, - cAccumRedBits: 0, - cAccumGreenBits: 0, - cAccumBlueBits: 0, - cAccumAlphaBits: 0, - cDepthBits: 24, - cStencilBits: 8, - cAuxBuffers: 0, - iLayerType: PFD_MAIN_PLANE, - bReserved: 0, - dwLayerMask: 0, - dwVisibleMask: 0, - dwDamageMask: 0, - }; +#[derive(Debug)] +pub struct NotCurrentContext; - // now querying - let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) }; - if pf_id == 0 { - return Err(CreationError::OsError("No available pixel format".to_owned())); - } +pub struct PossiblyCurrentContext; - Ok(pf_id) -} +#[derive(Debug)] +pub struct Config; diff --git a/glutin/src/config.rs b/glutin/src/config.rs new file mode 100644 index 00000000000..dfd62ccaae4 --- /dev/null +++ b/glutin/src/config.rs @@ -0,0 +1,352 @@ +//! GL config picking and creating utils. + +use std::ffi; +use std::num::NonZeroU32; + +use bitflags::bitflags; + +#[cfg(feature = "x11")] +use crate::platform_impl::x11::X11VisualInfo; +use crate::platform_impl::Config as PlatformConfig; + +/// Raw config. +pub enum RawConfig { + /// Raw EGL config. + #[cfg(feature = "egl")] + Egl(*const ffi::c_void), + + /// Raw GLX config. + #[cfg(feature = "glx")] + Glx(*const ffi::c_void), +} + +pub trait AsRawConfig { + // Obtain the [`RawConfig`] of the underlying Api. + fn raw_config(&self) -> RawConfig; +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + self.pimpl.raw_config() + } +} + +/// The GL configuration used to create [`Surface`] and [`Context`] in a cross platform way. +pub struct Config { + pub(crate) pimpl: PlatformConfig, +} + +impl Config { + pub(crate) fn from_platform(pimpl: PlatformConfig) -> Self { + Self { pimpl } + } + + // The `X11VisualInfo` that must be used to inititalize the Xlib window. + #[cfg(feature = "x11")] + pub fn x11_visual(&self) -> Option { + self.pimpl.x11_visual() + } + + /// The type of the underlying color buffer. + pub fn color_buffer_type(&self) -> ColorBufferType { + self.pimpl.color_buffer_type() + } + + /// The native visual identifier. + pub fn native_visual(&self) -> u32 { + self.pimpl.native_visual() + } + + /// The size of the alpha. + pub fn alpha_size(&self) -> u8 { + self.pimpl.alpha_size() + } + + /// The size of the depth buffer. + pub fn depth_size(&self) -> u8 { + self.pimpl.depth_size() + } + + /// The size of the stencil buffer. + pub fn stencil_size(&self) -> u8 { + self.pimpl.stencil_size() + } + + /// The number of sample buffers used for multisampling. + pub fn sample_buffers(&self) -> u8 { + self.pimpl.sample_buffers() + } + + /// Whether the config supports creating srgb capable [`Surface`]. + pub fn srgb_capable(&self) -> bool { + self.pimpl.srgb_capable() + } + + /// The type of the surfaces that can be created with this config. + pub fn config_surface_types(&self) -> ConfigSurfaceTypes { + self.pimpl.config_surface_types() + } + + /// The [`Api`] supported by the configuration. + pub fn api(&self) -> Api { + self.pimpl.api() + } +} + +bitflags! { + pub struct ConfigSurfaceTypes: u8 { + /// Context must support windows. + const WINDOW = 0b00000001; + + /// Context must support pixmaps. + const PIXMAP = 0b00000010; + + /// Context must support pbuffers. + const PBUFFER = 0b00000100; + } +} + +bitflags! { + pub struct Api : u8 { + /// Context supports OpenGL API. + const OPENGL = 0b00000001; + + /// Context supports OpenGL ES 1 API. + const GLES1 = 0b00000010; + + /// Context supports OpenGL ES 2 API. + const GLES2 = 0b00000100; + + /// Context supports OpenGL ES 3 API. + const GLES3 = 0b00001000; + } +} + +/// The buffer type baked by the config. +#[derive(Debug, Clone, Copy)] +pub enum ColorBufferType { + /// The backing buffer is using RGB format. + Rgb { r_size: u8, g_size: u8, b_size: u8 }, + + /// The backing buffer is using Luminance. + Luminance(u8), +} + +/// The context configuration template that is used to find desired config. +pub struct ConfigTemplate { + /// The type of the backing buffer and anccilary buffers. + pub(crate) color_buffer_type: ColorBufferType, + + /// Bits of alpha in the color buffer. + pub(crate) alpha_size: u8, + + /// Bits of depth in the depth buffer. + pub(crate) depth_size: u8, + + /// Bits of stencil in the stencil buffer. + pub(crate) stencil_size: u8, + + /// The amount of sample buffers. + pub(crate) sample_buffers: u8, + + /// The minimum swap interval supported by the configuration. + pub(crate) min_swap_interval: Option, + + /// The maximum swap interval supported by the configuration. + pub(crate) max_swap_interval: Option, + + /// The types of the surfaces supported by the configuration. + pub(crate) config_surface_types: ConfigSurfaceTypes, + + /// The rendering Apis supported by the configuration. + pub(crate) api: Api, + + /// The config should support transparency. + pub(crate) transparency: bool, + + /// The config supports double buffering. + pub(crate) single_buffering: bool, + + /// The config suppots stereoscopy. + pub(crate) stereoscopy: Option, + + /// The maximum width of the pbuffer. + pub(crate) max_pbuffer_width: Option, + + /// The maximum height of the pbuffer. + pub(crate) max_pbuffer_height: Option, +} + +impl ConfigTemplate { + fn new() -> Self { + Default::default() + } +} + +impl Default for ConfigTemplate { + fn default() -> Self { + ConfigTemplate { + color_buffer_type: ColorBufferType::Rgb { r_size: 8, g_size: 8, b_size: 8 }, + + alpha_size: 8, + + depth_size: 0, + + stencil_size: 0, + + sample_buffers: 0, + + transparency: true, + + stereoscopy: None, + + min_swap_interval: None, + + max_swap_interval: None, + + single_buffering: false, + + config_surface_types: ConfigSurfaceTypes::WINDOW, + + max_pbuffer_width: None, + max_pbuffer_height: None, + + api: Api::OPENGL, + } + } +} + +/// Builder for the [`ConfigTemplate`]. +#[derive(Default)] +pub struct ConfigTemplateBuilder { + template: ConfigTemplate, +} + +impl ConfigTemplateBuilder { + #[inline] + pub fn new() -> Self { + Default::default() + } + + /// Number of alpha bits in the color buffer. + /// + /// By default `8` is requested. + #[inline] + pub fn with_alpha_size(mut self, alpha_size: u8) -> Self { + self.template.alpha_size = alpha_size; + self + } + + /// Number of bits in the stencil buffer. + /// + /// By default `0` is requested. + #[inline] + pub fn with_stencil_size(mut self, stencil_size: u8) -> Self { + self.template.stencil_size = stencil_size; + self + } + + /// Number of bits in the depth buffer. + /// + /// By default `0` is requested. + #[inline] + pub fn with_depth_size(mut self, depth_size: u8) -> Self { + self.template.depth_size = depth_size; + self + } + + /// Number of sample buffers. + /// + /// By default `0` is requested. + #[inline] + pub fn with_sample_buffers(mut self, sample_buffers: u8) -> Self { + self.template.sample_buffers = sample_buffers; + self + } + + /// The types of the surfaces that must be supported by the configuration. + /// + /// By default only the `WINDOW` bit is set. + #[inline] + pub fn with_surface_type(mut self, config_surface_types: ConfigSurfaceTypes) -> Self { + self.template.config_surface_types = config_surface_types; + self + } + + /// The type of the color buffer. + /// + /// By default `RGB` buffer with all components sizes of `8` is requested. + #[inline] + pub fn with_buffer_type(mut self, color_buffer_type: ColorBufferType) -> Self { + self.template.color_buffer_type = color_buffer_type; + self + } + + /// The set of apis that are supported by configuration. + /// + /// By default `OpenGL` api is used. + #[inline] + pub fn with_api(mut self, api: Api) -> Self { + self.template.api = api; + self + } + + /// Wether the stereo pairs should be present. + /// + /// By default it isn't specified. + #[inline] + pub fn with_stereoscopy(mut self, stereoscopy: Option) -> Self { + self.template.stereoscopy = stereoscopy; + self + } + + /// Wether the single buffer should be used. + /// + /// By default `false` is requested. + #[inline] + pub fn with_single_buffering(mut self, single_buffering: bool) -> Self { + self.template.single_buffering = single_buffering; + self + } + + /// Wether the configuration should support transparency. + /// + /// The default is `false`. + /// + /// # Api specific + /// + /// EGL on X11 doesn't provide a way to create a transparent surface. Use GLX for that instead. + #[inline] + pub fn with_transparency(mut self, transparency: bool) -> Self { + self.template.transparency = transparency; + self + } + + /// With the maximum sizes of pbuffer. + #[inline] + pub fn with_pbuffer_sizes(mut self, width: NonZeroU32, height: NonZeroU32) -> Self { + self.template.max_pbuffer_width = Some(width.into()); + self.template.max_pbuffer_height = Some(height.into()); + self + } + + /// With supported swap intervals. + /// + /// By default the value isn't specified. + #[inline] + pub fn with_swap_interval( + mut self, + min_swap_interval: Option, + max_swap_interval: Option, + ) -> Self { + self.template.min_swap_interval = min_swap_interval; + self.template.max_swap_interval = max_swap_interval; + self + } + + /// Build the template to match the configs against. + #[must_use] + pub fn build(self) -> ConfigTemplate { + self.template + } +} diff --git a/glutin/src/context.rs b/glutin/src/context.rs index 7a4ae0f833e..b8db8fec34c 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -1,204 +1,202 @@ -use super::*; - +use std::cell::Cell; +use std::ffi::{self, CStr}; use std::marker::PhantomData; -use winit::event_loop::EventLoopWindowTarget; - -/// Represents an OpenGL [`Context`]. -/// -/// A [`Context`] is normally associated with a single Window, however -/// [`Context`]s can be *shared* between multiple windows or be headless. -/// -/// If a [`Context`] is backed by a window, it will be wrapped by either -/// [`RawContext`] or [`WindowedContext`]. -/// -/// # Example -/// -/// ```no_run -/// # fn main() { -/// # let el = glutin::event_loop::EventLoop::new(); -/// # let wb = glutin::window::WindowBuilder::new(); -/// # let some_context = glutin::ContextBuilder::new() -/// # .build_windowed(wb, &el) -/// # .unwrap(); -/// let cb = glutin::ContextBuilder::new() -/// .with_vsync(true) -/// .with_multisampling(8) -/// .with_shared_lists(some_context.context()); -/// # } -/// ``` -/// -/// [`WindowedContext`]: type.WindowedContext.html -/// [`RawContext`]: type.RawContext.html -/// [`Context`]: struct.Context.html -#[derive(Debug)] -pub struct Context { - pub(crate) context: platform_impl::Context, - pub(crate) phantom: PhantomData, -} - -impl Context { - /// See [`ContextWrapper::make_current`]. - /// - /// [`ContextWrapper::make_current`]: - /// struct.ContextWrapper.html#method.make_current - pub unsafe fn make_current(self) -> Result, (Self, ContextError)> { - match self.context.make_current() { - Ok(()) => Ok(Context { context: self.context, phantom: PhantomData }), - Err(err) => Err((Context { context: self.context, phantom: PhantomData }, err)), - } - } - - /// See [`ContextWrapper::make_not_current`]. - /// - /// [`ContextWrapper::make_not_current`]: - /// struct.ContextWrapper.html#method.make_not_current - pub unsafe fn make_not_current(self) -> Result, (Self, ContextError)> { - match self.context.make_not_current() { - Ok(()) => Ok(Context { context: self.context, phantom: PhantomData }), - Err(err) => Err((Context { context: self.context, phantom: PhantomData }, err)), - } - } - - /// See [`ContextWrapper::treat_as_not_current`]. - /// - /// [`ContextWrapper::treat_as_not_current`]: - /// struct.ContextWrapper.html#method.treat_as_not_current - pub unsafe fn treat_as_not_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } - } - - /// See [`ContextWrapper::treat_as_current`]. - /// - /// [`ContextWrapper::treat_as_current`]: - /// struct.ContextWrapper.html#method.treat_as_current - pub unsafe fn treat_as_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } - } - - /// See [`ContextWrapper::is_current`]. - /// - /// [`ContextWrapper::is_current`]: - /// struct.ContextWrapper.html#method.is_current - pub fn is_current(&self) -> bool { - self.context.is_current() - } - - /// See [`ContextWrapper::get_api`]. - /// - /// [`ContextWrapper::get_api`]: struct.ContextWrapper.html#method.get_api - pub fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl Context { - /// See [`ContextWrapper::get_proc_address`]. - /// - /// [`ContextWrapper::get_proc_address`]: - /// struct.ContextWrapper.html#method.get_proc_address - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.context.get_proc_address(addr) - } -} - -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Builds the given GL context. - /// - /// When on a unix operating system, prefer [`build_surfaceless`]. If both - /// [`build_surfaceless`] and `build_headless` fail, try using a hidden - /// window, or [`build_osmesa`]. Please note that if you choose to use a - /// hidden window, you must still handle the events it generates on the - /// events loop. - /// - /// Errors can occur in two scenarios: - /// - If the window could not be created (via permission denied, - /// incompatible system, out of memory, etc.). This should be very rare. - /// - If the OpenGL [`Context`] could not be created. This generally - /// happens - /// because the underlying platform doesn't support a requested feature. - /// - /// [`Context`]: struct.Context.html - #[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\ - [`build_surfaceless`]: platform/index.html\n\ - [`build_osmesa`]: platform/index.html\ - " - )] - #[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ - [`build_surfaceless`]: platform/unix/trait.HeadlessContextExt.html#tymethod.build_surfaceless\n\ - [`build_osmesa`]: platform/unix/trait.HeadlessContextExt.html#tymethod.build_osmesa\ - " - )] - pub fn build_headless( +use std::rc::Rc; + +use crate::platform_impl::{ + NotCurrentContext as PlatformNotCurrentContext, + PossiblyCurrentContext as PlatformPossiblyCurrentContext, +}; +use crate::surface::{Surface, SurfaceTypeTrait}; + +/// Raw context. +pub enum RawContext { + /// Raw EGL context. + Egl(*const ffi::c_void), + + /// Raw GLX context. + Glx(*const ffi::c_void), +} + +pub trait AsRawContext { + fn raw_context(&self) -> RawContext; +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + self.pimpl.raw_context() + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + self.pimpl.raw_context() + } +} + +// This one is not Send. +pub struct PossiblyCurrentContext { + pimpl: PlatformPossiblyCurrentContext, + // The context could be current only on the one thread. + _nosendsync: PhantomData>, +} + +pub struct NotCurrentContext { + pimpl: PlatformNotCurrentContext, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl NotCurrentContext { + pub(crate) fn from_platform(context: PlatformNotCurrentContext) -> Self { + Self { pimpl: context, _nosync: PhantomData } + } + + pub fn make_current(self, surface: &Surface) -> PossiblyCurrentContext { + let context = self.pimpl.make_current(&surface.pimpl); + PossiblyCurrentContext { pimpl: context, _nosendsync: PhantomData } + } + + pub fn make_current_draw_read( self, - el: &EventLoopWindowTarget, - size: dpi::PhysicalSize, - ) -> Result, CreationError> { - let ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - platform_impl::Context::new_headless(el, &pf_reqs, &gl_attr, size) - .map(|context| Context { context, phantom: PhantomData }) - } -} - -// This is nightly only: -// impl !Send for Context {} -// impl !Sync for Context {} -// -// Instead we add a phantom type to PossiblyCurrent - -/// A type that [`Context`]s which might possibly be currently current on some -/// thread take as a generic. -/// -/// See [`ContextWrapper::make_current`] for more details. -/// -/// [`ContextWrapper::make_current`]: -/// struct.ContextWrapper.html#method.make_current -/// [`Context`]: struct.Context.html -#[derive(Debug, Clone, Copy)] -pub struct PossiblyCurrent { - phantom: PhantomData<*mut ()>, -} - -/// A type that [`Context`]s which are not currently current on any thread take -/// as a generic. -/// -/// See [`ContextWrapper::make_current`] for more details. -/// -/// [`ContextWrapper::make_current`]: -/// struct.ContextWrapper.html#method.make_current -/// [`Context`]: struct.Context.html -#[derive(Debug, Clone, Copy)] -pub enum NotCurrent {} - -/// A trait implemented on both [`NotCurrent`] and -/// [`PossiblyCurrent`]. -/// -/// [`NotCurrent`]: enum.NotCurrent.html -/// [`PossiblyCurrent`]: struct.PossiblyCurrent.html -pub trait ContextCurrentState: std::fmt::Debug + Clone {} - -impl ContextCurrentState for PossiblyCurrent {} -impl ContextCurrentState for NotCurrent {} - -trait FailToCompileIfNotSendSync -where - Self: Send + Sync, -{ -} -impl FailToCompileIfNotSendSync for Context {} + surface_draw: &Surface, + surface_read: &Surface, + ) -> PossiblyCurrentContext { + let context = self.pimpl.make_current_draw_read(&surface_draw.pimpl, &surface_read.pimpl); + PossiblyCurrentContext { pimpl: context, _nosendsync: PhantomData } + } + + pub fn treat_as_current(self) -> PossiblyCurrentContext { + let context = self.pimpl.treat_as_current(); + PossiblyCurrentContext { pimpl: context, _nosendsync: PhantomData } + } +} + +impl PossiblyCurrentContext { + pub fn is_current(&self) -> bool { + self.pimpl.is_current() + } + + pub fn make_current(&self, surface: &Surface) { + self.pimpl.make_current(&surface.pimpl); + } + + pub fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + self.pimpl.make_current_draw_read(&surface_draw.pimpl, &surface_read.pimpl) + } + + pub fn make_not_current(self) -> NotCurrentContext { + let context = self.pimpl.make_not_current(); + NotCurrentContext { pimpl: context, _nosync: PhantomData } + } + + pub fn set_swap_interval(&self, interval: u16) { + unimplemented!() + } + + pub fn update_after_resize(&self) { + self.pimpl.update_after_resize() + } + + pub fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + self.pimpl.get_proc_address(addr) + } +} + +impl ContextAttributes { + pub(crate) fn new() -> Self { + Default::default() + } +} + +pub enum ReleaseBehavior { + None, + + Flush, +} + +impl Default for ReleaseBehavior { + fn default() -> Self { + ReleaseBehavior::Flush + } +} + +pub enum GlProfile { + Core, + + Compatibility, +} + +pub enum Robustness { + NotRobust, + + NoError, + + RobustNoResetNotification, + + RobustLostContextOnReset, +} + +impl Default for Robustness { + fn default() -> Self { + Robustness::NotRobust + } +} + +/// The attributes that are used to create a graphics context. +#[derive(Default)] +pub struct ContextAttributes { + pub(crate) release_behavior: ReleaseBehavior, + + pub(crate) debug: bool, + + pub(crate) robustness: Robustness, + + pub(crate) profile: Option, + + pub(crate) shared_context: Option, +} + +#[derive(Default)] +pub struct ContextAttributesBuilder { + attributes: ContextAttributes, +} + +impl ContextAttributesBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn with_debug(mut self, debug: bool) -> Self { + self.attributes.debug = debug; + self + } + + pub fn with_sharing(mut self, context: &impl AsRawContext) -> Self { + self.attributes.shared_context = Some(context.raw_context()); + self + } + + pub fn with_robustness(mut self, robustness: Robustness) -> Self { + self.attributes.robustness = robustness; + self + } + + pub fn with_profile(mut self, profile: GlProfile) -> Self { + self.attributes.profile = Some(profile); + self + } + + pub fn with_release_behavior(mut self, release_behavior: ReleaseBehavior) -> Self { + self.attributes.release_behavior = release_behavior; + self + } + + pub fn build(self) -> ContextAttributes { + self.attributes + } +} diff --git a/glutin/src/display.rs b/glutin/src/display.rs new file mode 100644 index 00000000000..a6cc1ca3158 --- /dev/null +++ b/glutin/src/display.rs @@ -0,0 +1,170 @@ +//! The GL platform display creation and picking. + +use std::ffi; +use std::sync::Arc; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::{Config, ConfigTemplate}; +use crate::context::{ContextAttributes, NotCurrentContext}; +use crate::error::Result; +use crate::surface::{PbufferSurface, PixmapSurface, Surface, SurfaceAttributes, WindowSurface}; + +use crate::platform_impl::Display as PlatformDisplay; + +#[cfg(feature = "glx")] +use crate::api::glx::XlibErrorHookRegistrator; + +/// The graphics display to handle underlying graphics platform in a crossplatform way. +#[derive(Clone)] +pub struct Display { + pimpl: Arc, +} + +/// Raw GL platform display. +pub enum RawDisplay { + /// Raw EGL display. + #[cfg(feature = "egl")] + Egl(*const ffi::c_void), + + /// Raw GLX display. + #[cfg(feature = "glx")] + Glx(*const ffi::c_void), +} + +pub trait AsRawDisplay { + /// A raw handle to the underlying api display. + fn raw_display(&self) -> RawDisplay; +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + self.pimpl.raw_display() + } +} + +/// Preference of the display that should be used. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum DisplayApiPreference { + /// Prefer EGL. + #[cfg(feature = "egl")] + Egl, + /// Prefer GLX. + #[cfg(feature = "glx")] + Glx, + /// Prefer EGL and fallback to GLX. + #[cfg(all(feature = "egl", feature = "glx"))] + EglThenGlx, + /// Prefer GLX and fallback to EGL. + #[cfg(all(feature = "egl", feature = "glx"))] + GlxThenEgl, +} + +/// A settings to control automatic display picking. +pub struct DisplayPicker { + pub(crate) api_preference: DisplayApiPreference, + #[cfg(feature = "glx")] + pub(crate) glx_error_registrator: Option, +} + +impl Default for DisplayPicker { + #[cfg(all(feature = "egl", feature = "glx"))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl, glx_error_registrator: None } + } + + #[cfg(all(feature = "egl", not(feature = "glx")))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl } + } + + #[cfg(all(feature = "glx", not(feature = "egl")))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Glx, glx_error_registrator: None } + } +} + +impl DisplayPicker { + pub fn new() -> Self { + Default::default() + } + + /// The preference of the underlying system Api. + pub fn with_api_preference(mut self, api_preference: DisplayApiPreference) -> Self { + self.api_preference = api_preference; + self + } + + /// The hook to register glutin error handler in X11 error handling function. + /// + /// The hook registrator must be provided in case GLX will be used. + #[cfg(feature = "glx")] + pub fn with_glx_error_registrator( + mut self, + error_registrator: XlibErrorHookRegistrator, + ) -> Self { + self.glx_error_registrator = Some(error_registrator); + self + } +} + +impl Display { + /// Create a graphics platform display from the given raw display handle. + /// + /// # Safety + /// + /// The `display` must point to the valid platform display. + pub unsafe fn from_raw(display: RawDisplayHandle, picker: DisplayPicker) -> Result { + let display = PlatformDisplay::from_raw(display, picker)?; + Ok(Self { pimpl: Arc::new(display) }) + } + + /// Find configuration matching the given `template`. + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + let configs = self.pimpl.find_configs(template)?; + Some(Box::new(configs.map(Config::from_platform))) + } + + /// Create the graphics platform context. + pub fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let context = self.pimpl.create_context(&config.pimpl, context_attributes)?; + Ok(NotCurrentContext::from_platform(context)) + } + + /// Create the surface that can be used to render into native window. + pub fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let surface = self.pimpl.create_window_surface(&config.pimpl, surface_attributes)?; + Ok(Surface::from_platform(surface)) + } + + /// Create the surfate that can be used to render into pbuffer. + pub fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let surface = self.pimpl.create_pbuffer_surface(&config.pimpl, surface_attributes)?; + Ok(Surface::from_platform(surface)) + } + + /// Create the surface that can be used to render into pixmap. + pub fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + let surface = self.pimpl.create_pixmap_surface(&config.pimpl, surface_attributes)?; + Ok(Surface::from_platform(surface)) + } +} diff --git a/glutin/src/error.rs b/glutin/src/error.rs new file mode 100644 index 00000000000..d84a299bbd9 --- /dev/null +++ b/glutin/src/error.rs @@ -0,0 +1,160 @@ +use std::fmt; + +/// A specialized [`Result`] type for graphics operations. +pub type Result = std::result::Result; + +/// The error type for all the graphics platform operations. +#[derive(Clone, Debug)] +pub struct Error { + /// The raw code of the underlying error. + raw_code: Option, + + /// The raw message from the os in case it could be obtained. + raw_os_message: Option, + + /// The simplified error kind to handle mathing. + kind: ErrorKind, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(raw_code) = self.raw_code { + write!(f, "[{:x}] ", raw_code)?; + } + + let msg = if let Some(raw_os_message) = self.raw_os_message.as_ref() { + raw_os_message + } else { + self.kind.as_str() + }; + + write!(f, "{}", msg) + } +} + +impl Error { + pub(crate) fn new(raw_code: i64, raw_os_message: Option, kind: ErrorKind) -> Self { + Self { raw_code: Some(raw_code), raw_os_message, kind } + } + + /// The underlying error kind. + #[inline] + pub fn error_kind(&self) -> ErrorKind { + self.kind + } + + /// The underlying raw code if it's present. + #[inline] + pub fn raw_code(&self) -> Option { + self.raw_code + } +} + +impl std::error::Error for Error {} + +/// Build an error with just a kind. +impl From for Error { + fn from(kind: ErrorKind) -> Self { + Error { raw_code: None, raw_os_message: None, kind } + } +} + +/// A list specifying general categoires of native platform graphics interface errors. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum ErrorKind { + /// The requested resource wasn't found. + NotFound, + + /// Failed to perform resource initialization. + InitializationFailed, + + /// Can't access a requested resource. + /// + /// For example when trying to make a context current while it's current on another thread. + BadAccess, + + /// An operation could not be completed, because it failed to allocate enough memory. + OutOfMemory, + + /// Anrecognized attribute value was passed. + BadAttribute, + + /// The context is no longer valid. + BadContext, + + /// The context is in bad state. + BadContextState, + + /// Invalid config was passed. + BadConfig, + + /// The current surface of the calling thread is no longer valid. + BadCurrentSurface, + + /// The display is no longer valid. + BadDisplay, + + /// The surface is invalid. + BadSurface, + + /// The pbuffer is invalid. + BadPbuffer, + + /// The pixmap is invalid. + BadPixmap, + + /// Arguments are inconsistent. For example when sharad context are not compatible. + BadMatch, + + /// One or more argument values are invalid. + BadParameter, + + /// Bad native pixmap was provided. + BadNativePixmap, + + /// Bad native window was provided. + BadNativeWindow, + + /// The context was lost. + ContextLost, + + /// The operation is not supported by the platform. + NotSupported, + + /// The misc error that can't be classyfied occured. + Misc, +} + +impl ErrorKind { + pub(crate) fn as_str(&self) -> &'static str { + use ErrorKind::*; + match *self { + NotFound => "not found", + InitializationFailed => "initialization failed", + BadAccess => "access to the resource failed", + OutOfMemory => "out of memory", + BadAttribute => "an anrecougnized attribute or attribute value was passed", + BadContext => "argument does not name a valid context", + BadContextState => "the context is in a bad state", + BadConfig => "argument does not name a valid config", + BadCurrentSurface => "the current surface of the calling thread is no longer valid", + BadDisplay => "argument does not name a valid display", + BadSurface => "argument does not name a valid surface", + BadPbuffer => "argument does not name a valid pbuffer", + BadPixmap => "argument does not name a valid pixmap", + BadMatch => "arguments are inconsistance", + BadParameter => "one or more argument values are invalid", + BadNativePixmap => "argument does not refer to a valid native pixmap", + BadNativeWindow => "argument does not refer to a valid native window", + ContextLost => "context loss", + NotSupported => "not supported", + Misc => "misc platform error", + } + } +} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs index 1ac4d8c96dc..686ed55ff92 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -1,713 +1,28 @@ -//! The purpose of this library is to provide an OpenGL [`Context`] on as many -//! platforms as possible. -//! -//! # Building a [`WindowedContext`] -//! -//! A [`WindowedContext`] is composed of a [`Window`] and an OpenGL -//! [`Context`]. -//! -//! Due to some operating-system-specific quirks, glutin prefers control over -//! the order of creation of the [`Context`] and [`Window`]. Here is an example -//! of building a [`WindowedContext`]: -//! -//! ```no_run -//! # fn main() { -//! let el = glutin::event_loop::EventLoop::new(); -//! let wb = glutin::window::WindowBuilder::new() -//! .with_title("Hello world!") -//! .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); -//! let windowed_context = glutin::ContextBuilder::new() -//! .build_windowed(wb, &el) -//! .unwrap(); -//! # } -//! ``` -//! -//! You can, of course, create a [`RawContext`] separately from an existing -//! window, however that may result in an suboptimal configuration of the window -//! on some platforms. In that case use the unsafe platform-specific -//! [`RawContextExt`] available on unix operating systems and Windows. -//! -//! You can also produce headless [`Context`]s via the -//! [`ContextBuilder::build_headless`] function. -//! -//! [`Window`]: window/struct.Window.html -//! [`Context`]: struct.Context.html -//! [`WindowedContext`]: type.WindowedContext.html -//! [`RawContext`]: type.RawContext.html -#![cfg_attr( - target_os = "windows", - doc = "\ -[`RawContextExt`]: os/windows/trait.RawContextExt.html -" -)] -#![cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "windows", - target_os = "openbsd", - )), - doc = "\ -[`RawContextExt`]: os/index.html -" -)] -#![cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`RawContextExt`]: os/unix/trait.RawContextExt.html -" -)] -#![deny( - missing_debug_implementations, - //missing_docs, -)] +//! TODO write something here. + +pub mod api; +pub mod config; +pub mod context; +pub mod display; +pub mod error; +pub mod surface; + +mod lib_loading; #[cfg(any( - target_os = "windows", target_os = "linux", - target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", ))] -#[macro_use] -extern crate lazy_static; -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[macro_use] -extern crate objc; - -pub mod platform; - -mod api; -mod context; +#[path = "platform_impl/linux/mod.rs"] mod platform_impl; -mod windowed; - -pub use crate::context::*; -pub use crate::windowed::*; -pub use winit::*; - -use winit::error::OsError; - -use std::io; - -/// An object that allows you to build [`Context`]s, [`RawContext`]s and -/// [`WindowedContext`]s. -/// -/// One notable limitation of the Wayland backend when it comes to shared -/// [`Context`]s is that both contexts must use the same events loop. -/// -/// [`Context`]: struct.Context.html -/// [`WindowedContext`]: type.WindowedContext.html -/// [`RawContext`]: type.RawContext.html -#[derive(Debug, Clone)] -pub struct ContextBuilder<'a, T: ContextCurrentState> { - /// The attributes to use to create the context. - pub gl_attr: GlAttributes<&'a Context>, - /// The pixel format requirements - pub pf_reqs: PixelFormatRequirements, -} - -impl<'a> ContextBuilder<'a, NotCurrent> { - /// Initializes a new `ContextBuilder` with default values. - pub fn new() -> Self { - ContextBuilder { - pf_reqs: std::default::Default::default(), - gl_attr: std::default::Default::default(), - } - } -} - -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Sets how the backend should choose the OpenGL API and version. - #[inline] - pub fn with_gl(mut self, request: GlRequest) -> Self { - self.gl_attr.version = request; - self - } - - /// Sets the desired OpenGL [`Context`] profile. - /// - /// [`Context`]: struct.Context.html - #[inline] - pub fn with_gl_profile(mut self, profile: GlProfile) -> Self { - self.gl_attr.profile = Some(profile); - self - } - - /// Sets the *debug* flag for the OpenGL [`Context`]. - /// - /// The default value for this flag is `cfg!(debug_assertions)`, which means - /// that it's enabled when you run `cargo build` and disabled when you run - /// `cargo build --release`. - /// - /// [`Context`]: struct.Context.html - #[inline] - pub fn with_gl_debug_flag(mut self, flag: bool) -> Self { - self.gl_attr.debug = flag; - self - } - - /// Sets the robustness of the OpenGL [`Context`]. See the docs of - /// [`Robustness`]. - /// - /// [`Context`]: struct.Context.html - /// [`Robustness`]: enum.Robustness.html - #[inline] - pub fn with_gl_robustness(mut self, robustness: Robustness) -> Self { - self.gl_attr.robustness = robustness; - self - } - - /// Requests that the window has vsync enabled. - /// - /// By default, vsync is not enabled. - #[inline] - pub fn with_vsync(mut self, vsync: bool) -> Self { - self.gl_attr.vsync = vsync; - self - } - - /// Share the display lists with the given [`Context`]. - /// - /// [`Context`]: struct.Context.html - #[inline] - pub fn with_shared_lists( - self, - other: &'a Context, - ) -> ContextBuilder<'a, T2> { - ContextBuilder { gl_attr: self.gl_attr.set_sharing(Some(other)), pf_reqs: self.pf_reqs } - } - - /// Sets the multisampling level to request. A value of `0` indicates that - /// multisampling must not be enabled. - /// - /// # Panic - /// - /// Will panic if `samples` is not a power of two. - #[inline] - pub fn with_multisampling(mut self, samples: u16) -> Self { - self.pf_reqs.multisampling = match samples { - 0 => None, - _ => { - assert!(samples.is_power_of_two()); - Some(samples) - } - }; - self - } - - /// Sets the number of bits in the depth buffer. - #[inline] - pub fn with_depth_buffer(mut self, bits: u8) -> Self { - self.pf_reqs.depth_bits = Some(bits); - self - } - - /// Sets the number of bits in the stencil buffer. - #[inline] - pub fn with_stencil_buffer(mut self, bits: u8) -> Self { - self.pf_reqs.stencil_bits = Some(bits); - self - } - - /// Sets the number of bits in the color buffer. - #[inline] - pub fn with_pixel_format(mut self, color_bits: u8, alpha_bits: u8) -> Self { - self.pf_reqs.color_bits = Some(color_bits); - self.pf_reqs.alpha_bits = Some(alpha_bits); - self - } - - /// Request the backend to be stereoscopic. - #[inline] - pub fn with_stereoscopy(mut self) -> Self { - self.pf_reqs.stereoscopy = true; - self - } - - /// Sets whether sRGB should be enabled on the window. - /// - /// The default value is `true`. - #[inline] - pub fn with_srgb(mut self, srgb_enabled: bool) -> Self { - self.pf_reqs.srgb = srgb_enabled; - self - } - - /// Sets whether double buffering should be enabled. - /// - /// The default value is `None`. - /// - /// ## Platform-specific - /// - /// This option will be taken into account on the following platforms: - /// - /// * MacOS - /// * Unix operating systems using GLX with X - /// * Windows using WGL - #[inline] - pub fn with_double_buffer(mut self, double_buffer: Option) -> Self { - self.pf_reqs.double_buffer = double_buffer; - self - } - - /// Sets whether hardware acceleration is required. - /// - /// The default value is `Some(true)` - /// - /// ## Platform-specific - /// - /// This option will be taken into account on the following platforms: - /// - /// * MacOS - /// * Unix operating systems using EGL with either X or Wayland - /// * Windows using EGL or WGL - /// * Android using EGL - #[inline] - pub fn with_hardware_acceleration(mut self, acceleration: Option) -> Self { - self.pf_reqs.hardware_accelerated = acceleration; - self - } -} - -/// Error that can happen while creating a window or a headless renderer. -#[derive(Debug)] -pub enum CreationError { - OsError(String), - NotSupported(String), - NoBackendAvailable(Box), - RobustnessNotSupported, - OpenGlVersionNotSupported, - NoAvailablePixelFormat, - PlatformSpecific(String), - Window(OsError), - /// We received multiple errors, instead of one. - CreationErrors(Vec>), -} - -impl CreationError { - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - #[cfg(feature = "x11")] - pub(crate) fn append(self, err: CreationError) -> Self { - match self { - CreationError::CreationErrors(mut errs) => { - errs.push(Box::new(err)); - CreationError::CreationErrors(errs) - } - _ => CreationError::CreationErrors(vec![Box::new(err), Box::new(self)]), - } - } -} - -impl std::fmt::Display for CreationError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - f.write_str(match self { - CreationError::OsError(text) - | CreationError::NotSupported(text) - | CreationError::PlatformSpecific(text) => text, - CreationError::NoBackendAvailable(err) => { - return write!(f, "No backend is available: {}", err); - } - CreationError::RobustnessNotSupported => { - "You requested robustness, but it is not supported." - } - CreationError::OpenGlVersionNotSupported => { - "The requested OpenGL version is not supported." - } - CreationError::NoAvailablePixelFormat => { - "Couldn't find any pixel format that matches the criteria." - } - CreationError::Window(err) => { - return write!(f, "{}", err); - } - CreationError::CreationErrors(ref es) => { - writeln!(f, "Received multiple errors:")?; - for e in es { - writeln!(f, "\t{}", e)?; - } - return Ok(()); - } - }) - } -} - -impl std::error::Error for CreationError { - fn cause(&self) -> Option<&dyn std::error::Error> { - match *self { - CreationError::NoBackendAvailable(ref err) => Some(&**err), - CreationError::Window(ref err) => Some(err), - _ => None, - } - } -} - -impl From for CreationError { - fn from(err: OsError) -> Self { - CreationError::Window(err) - } -} - -/// Error that can happen when manipulating an OpenGL [`Context`]. -/// -/// [`Context`]: struct.Context.html -#[derive(Debug)] -pub enum ContextError { - /// General platform error. - OsError(String), - IoError(io::Error), - ContextLost, - FunctionUnavailable, -} - -impl std::fmt::Display for ContextError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - match self { - ContextError::OsError(string) => write!(formatter, "{}", string), - ContextError::IoError(err) => write!(formatter, "{}", err), - ContextError::ContextLost => write!(formatter, "Context lost"), - ContextError::FunctionUnavailable => write!(formatter, "Function unavailable"), - } - } -} - -impl std::error::Error for ContextError {} - -/// All APIs related to OpenGL that you can possibly get while using glutin. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Api { - /// The classical OpenGL. Available on Windows, Unix operating systems, - /// OS/X. - OpenGl, - /// OpenGL embedded system. Available on Unix operating systems, Android. - OpenGlEs, - /// OpenGL for the web. Very similar to OpenGL ES. - WebGl, -} - -/// Describes the requested OpenGL [`Context`] profiles. -/// -/// [`Context`]: struct.Context.html -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum GlProfile { - /// Include all the immediate more functions and definitions. - Compatibility, - /// Include all the future-compatible functions and definitions. - Core, -} - -/// Describes the OpenGL API and version that are being requested when a context -/// is created. -#[derive(Debug, Copy, Clone)] -pub enum GlRequest { - /// Request the latest version of the "best" API of this platform. - /// - /// On desktop, will try OpenGL. - Latest, - - /// Request a specific version of a specific API. - /// - /// Example: `GlRequest::Specific(Api::OpenGl, (3, 3))`. - Specific(Api, (u8, u8)), - /// If OpenGL is available, create an OpenGL [`Context`] with the specified - /// `opengl_version`. Else if OpenGL ES or WebGL is available, create a - /// context with the specified `opengles_version`. - /// - /// [`Context`]: struct.Context.html - GlThenGles { - /// The version to use for OpenGL. - opengl_version: (u8, u8), - /// The version to use for OpenGL ES. - opengles_version: (u8, u8), - }, -} - -impl GlRequest { - /// Extract the desktop GL version, if any. - pub fn to_gl_version(&self) -> Option<(u8, u8)> { - match self { - &GlRequest::Specific(Api::OpenGl, opengl_version) => Some(opengl_version), - &GlRequest::GlThenGles { opengl_version, .. } => Some(opengl_version), - _ => None, - } - } -} - -/// The minimum core profile GL context. Useful for getting the minimum -/// required GL version while still running on OSX, which often forbids -/// the compatibility profile features. -pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2)); - -/// Specifies the tolerance of the OpenGL [`Context`] to faults. If you accept -/// raw OpenGL commands and/or raw shader code from an untrusted source, you -/// should definitely care about this. -/// -/// [`Context`]: struct.Context.html -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Robustness { - /// Not everything is checked. Your application can crash if you do - /// something wrong with your shaders. - NotRobust, - - /// The driver doesn't check anything. This option is very dangerous. - /// Please know what you're doing before using it. See the - /// `GL_KHR_no_error` extension. - /// - /// Since this option is purely an optimization, no error will be returned - /// if the backend doesn't support it. Instead it will automatically - /// fall back to [`NotRobust`]. - /// - /// [`NotRobust`]: enum.Robustness.html#variant.NotRobust - NoError, - - /// Everything is checked to avoid any crash. The driver will attempt to - /// avoid any problem, but if a problem occurs the behavior is - /// implementation-defined. You are just guaranteed not to get a crash. - RobustNoResetNotification, - - /// Same as [`RobustNoResetNotification`] but the context creation doesn't - /// fail if it's not supported. - /// - /// [`RobustNoResetNotification`]: - /// enum.Robustness.html#variant.RobustNoResetNotification - TryRobustNoResetNotification, - - /// Everything is checked to avoid any crash. If a problem occurs, the - /// context will enter a "context lost" state. It must then be - /// recreated. For the moment, glutin doesn't provide a way to recreate - /// a context with the same window :-/ - RobustLoseContextOnReset, - - /// Same as [`RobustLoseContextOnReset`] but the context creation doesn't - /// fail if it's not supported. - /// - /// [`RobustLoseContextOnReset`]: - /// enum.Robustness.html#variant.RobustLoseContextOnReset - TryRobustLoseContextOnReset, -} - -/// The behavior of the driver when you change the current context. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ReleaseBehavior { - /// Doesn't do anything. Most notably doesn't flush. - None, - - /// Flushes the context that was previously current as if `glFlush` was - /// called. - Flush, -} - -/// Describes a possible format. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct PixelFormat { - pub hardware_accelerated: bool, - /// The number of color bits. Does not include alpha bits. - pub color_bits: u8, - pub alpha_bits: u8, - pub depth_bits: u8, - pub stencil_bits: u8, - pub stereoscopy: bool, - pub double_buffer: bool, - /// `None` if multisampling is disabled, otherwise `Some(N)` where `N` is - /// the multisampling level. - pub multisampling: Option, - pub srgb: bool, -} - -/// Describes how the backend should choose a pixel format. -// TODO: swap method? (swap, copy) -#[derive(Clone, Debug)] -pub struct PixelFormatRequirements { - /// If true, only hardware-accelerated formats will be considered. If - /// false, only software renderers. `None` means "don't care". Default - /// is `Some(true)`. - pub hardware_accelerated: Option, - - /// Minimum number of bits for the color buffer, excluding alpha. `None` - /// means "don't care". The default is `Some(24)`. - pub color_bits: Option, - - /// If true, the color buffer must be in a floating point format. Default - /// is `false`. - /// - /// Using floating points allows you to write values outside of the `[0.0, - /// 1.0]` range. - pub float_color_buffer: bool, - - /// Minimum number of bits for the alpha in the color buffer. `None` means - /// "don't care". The default is `Some(8)`. - pub alpha_bits: Option, - - /// Minimum number of bits for the depth buffer. `None` means "don't care". - /// The default value is `Some(24)`. - pub depth_bits: Option, - - /// Minimum number of stencil bits. `None` means "don't care". - /// The default value is `Some(8)`. - pub stencil_bits: Option, - - /// If true, only double-buffered formats will be considered. If false, - /// only single-buffer formats. `None` means "don't care". The default - /// is `Some(true)`. - pub double_buffer: Option, - - /// Contains the minimum number of samples per pixel in the color, depth - /// and stencil buffers. `None` means "don't care". Default is `None`. - /// A value of `Some(0)` indicates that multisampling must not be enabled. - pub multisampling: Option, - - /// If true, only stereoscopic formats will be considered. If false, only - /// non-stereoscopic formats. The default is `false`. - pub stereoscopy: bool, - - /// If true, only sRGB-capable formats will be considered. If false, don't - /// care. The default is `true`. - pub srgb: bool, - - /// The behavior when changing the current context. Default is `Flush`. - pub release_behavior: ReleaseBehavior, - - /// X11 only: set internally to insure a certain visual xid is used when - /// choosing the fbconfig. - pub(crate) x11_visual_xid: Option, -} - -impl Default for PixelFormatRequirements { - #[inline] - fn default() -> PixelFormatRequirements { - PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - float_color_buffer: false, - alpha_bits: Some(8), - depth_bits: Some(24), - stencil_bits: Some(8), - double_buffer: None, - multisampling: None, - stereoscopy: false, - srgb: true, - release_behavior: ReleaseBehavior::Flush, - x11_visual_xid: None, - } - } -} - -/// Attributes to use when creating an OpenGL [`Context`]. -/// -/// [`Context`]: struct.Context.html -#[derive(Clone, Debug)] -pub struct GlAttributes { - /// An existing context with which some OpenGL objects get shared. - /// - /// The default is `None`. - pub sharing: Option, - - /// Version to try create. See [`GlRequest`] for more infos. - /// - /// The default is [`Latest`]. - /// - /// [`Latest`]: enum.GlRequest.html#variant.Latest - /// [`GlRequest`]: enum.GlRequest.html - pub version: GlRequest, - - /// OpenGL profile to use. - /// - /// The default is `None`. - pub profile: Option, - - /// Whether to enable the `debug` flag of the context. - /// - /// Debug contexts are usually slower but give better error reporting. - /// - /// The default is `true` in debug mode and `false` in release mode. - pub debug: bool, - - /// How the OpenGL [`Context`] should detect errors. - /// - /// The default is `NotRobust` because this is what is typically expected - /// when you create an OpenGL [`Context`]. However for safety you should - /// consider [`TryRobustLoseContextOnReset`]. - /// - /// [`Context`]: struct.Context.html - /// [`TryRobustLoseContextOnReset`]: - /// enum.Robustness.html#variant.TryRobustLoseContextOnReset - pub robustness: Robustness, - - /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will - /// block until the screen refreshes. This is typically used to prevent - /// screen tearing. - /// - /// The default is `false`. - pub vsync: bool, -} - -impl GlAttributes { - /// Turns the `sharing` parameter into another type by calling a closure. - #[inline] - pub fn map_sharing(self, f: F) -> GlAttributes - where - F: FnOnce(S) -> T, - { - GlAttributes { - sharing: self.sharing.map(f), - version: self.version, - profile: self.profile, - debug: self.debug, - robustness: self.robustness, - vsync: self.vsync, - } - } - - /// Turns the `sharing` parameter into another type. - #[inline] - fn set_sharing(self, sharing: Option) -> GlAttributes { - GlAttributes { - sharing, - version: self.version, - profile: self.profile, - debug: self.debug, - robustness: self.robustness, - vsync: self.vsync, - } - } -} - -impl Default for GlAttributes { - #[inline] - fn default() -> GlAttributes { - GlAttributes { - sharing: None, - version: GlRequest::Latest, - profile: None, - debug: cfg!(debug_assertions), - robustness: Robustness::NotRobust, - vsync: false, - } - } -} +#[cfg(target_os = "android")] +#[path = "platform_impl/android/mod.rs"] +mod platform_impl; -// Rectangles to submit as buffer damage. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Rect { - pub x: u32, - pub y: u32, - pub width: u32, - pub height: u32, -} +#[cfg(windows)] +#[path = "platform_impl/windows/mod.rs"] +mod platform_impl; diff --git a/glutin/src/lib_loading.rs b/glutin/src/lib_loading.rs new file mode 100644 index 00000000000..fadf8315cc8 --- /dev/null +++ b/glutin/src/lib_loading.rs @@ -0,0 +1,46 @@ +//! Library loading routines. + +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +use libloading::Library; + +#[derive(Clone)] +pub struct SymWrapper { + sym: T, + _lib: Arc, +} + +pub trait SymLoading { + fn load_with(lib: &Library) -> Self; +} + +impl SymWrapper { + pub fn new(libs: &[&str]) -> Result { + unsafe { + for lib in libs { + if let Ok(lib) = Library::new(lib) { + return Ok(SymWrapper { sym: T::load_with(&lib), _lib: Arc::new(lib) }); + } + } + } + + Err(()) + } +} + +impl Deref for SymWrapper { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.sym + } +} + +impl DerefMut for SymWrapper { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.sym + } +} diff --git a/glutin/src/platform/android.rs b/glutin/src/platform/android.rs deleted file mode 100644 index 9da067af396..00000000000 --- a/glutin/src/platform/android.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![cfg(target_os = "android")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; - -pub use winit::platform::android::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = EGLContext; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - Some(self.context.get_egl_display()) - } -} diff --git a/glutin/src/platform/ios.rs b/glutin/src/platform/ios.rs deleted file mode 100644 index d9073015b88..00000000000 --- a/glutin/src/platform/ios.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![cfg(target_os = "ios")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; - -pub use winit::platform::ios::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = *mut raw::c_void; - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - None - } -} diff --git a/glutin/src/platform/macos.rs b/glutin/src/platform/macos.rs deleted file mode 100644 index 9e21e9211e2..00000000000 --- a/glutin/src/platform/macos.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![cfg(target_os = "macos")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; - -pub use winit::platform::macos::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = *mut raw::c_void; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - None - } -} diff --git a/glutin/src/platform/mod.rs b/glutin/src/platform/mod.rs deleted file mode 100644 index e508519a031..00000000000 --- a/glutin/src/platform/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Contains traits with platform-specific methods in them. -//! -//! Contains the following modules: -//! -//! - `android` -//! - `ios` -//! - `macos` -//! - `unix` -//! - `windows` - -/// Platform-specific methods for android. -pub mod android; -/// Platform-specific methods for iOS. -pub mod ios; -/// Platform-specific methods for macOS. -pub mod macos; -/// Platform-specific methods for unix operating systems. -pub mod unix; -/// Platform-specific methods for Windows. -pub mod windows; -/// Platform-specific methods for event loops independent from the application -/// lifetime. -pub mod run_return { - #![cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", - ))] - pub use winit::platform::run_return::*; -} - -use std::os::raw; - -/// Platform-specific extensions for OpenGL [`Context`]s. -/// -/// [`Context`]: ../struct.Context.html -pub trait ContextTraitExt { - /// Raw context handle. - type Handle; - - /// Returns the raw context handle. - unsafe fn raw_handle(&self) -> Self::Handle; - - /// Returns a pointer to the `EGLDisplay` object of EGL that is used by this - /// context. - /// - /// Return `None` if the context doesn't use EGL. - // The pointer will become invalid when the context is destroyed. - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void>; -} diff --git a/glutin/src/platform/unix.rs b/glutin/src/platform/unix.rs deleted file mode 100644 index 66aed2c4ad7..00000000000 --- a/glutin/src/platform/unix.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -use crate::platform::ContextTraitExt; -pub use crate::platform_impl::{HeadlessContextExt, RawContextExt, RawHandle}; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; -#[cfg(feature = "x11")] -pub use glutin_glx_sys::GLXContext; - -pub use winit::platform::unix::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = RawHandle; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - self.context.get_egl_display() - } -} diff --git a/glutin/src/platform/windows.rs b/glutin/src/platform/windows.rs deleted file mode 100644 index 583ef0d6328..00000000000 --- a/glutin/src/platform/windows.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![cfg(target_os = "windows")] - -use crate::platform::ContextTraitExt; -pub use crate::platform_impl::{RawContextExt, RawHandle}; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; - -pub use winapi::shared::windef::HGLRC; -pub use winit::platform::windows::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = RawHandle; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - self.context.get_egl_display() - } -} diff --git a/glutin/src/platform_impl/android/mod.rs b/glutin/src/platform_impl/android/mod.rs index 594914300ed..a40a38998ab 100644 --- a/glutin/src/platform_impl/android/mod.rs +++ b/glutin/src/platform_impl/android/mod.rs @@ -1,164 +1,7 @@ -#![cfg(target_os = "android")] +pub use crate::api::egl::{Config, Display, NotCurrentContext, PossiblyCurrentContext, Surface}; -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType}; -use crate::CreationError::{self, OsError}; -use crate::{Api, ContextError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect}; - -use glutin_egl_sys as ffi; -use parking_lot::Mutex; -use raw_window_handle::{AndroidNdkHandle, HasRawWindowHandle, RawWindowHandle}; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::WindowBuilder; - -use std::sync::Arc; - -#[derive(Debug)] -struct AndroidContext { - egl_context: EglContext, - stopped: Option>, -} - -#[derive(Debug)] -pub struct Context(Arc); - -impl Context { - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result<(winit::window::Window, Self), CreationError> { - let win = wb.build(el)?; - let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); - let nwin = - if let RawWindowHandle::AndroidNdk(AndroidNdkHandle { a_native_window, .. }) = - win.raw_window_handle() - { - a_native_window - } else { - return Err(OsError("raw_window_handle() is not for Android".to_string())); - }; - let native_display = NativeDisplay::Android; - let egl_context = - EglContext::new(pf_reqs, &gl_attr, native_display, EglSurfaceType::Window, |c, _| { - Ok(c[0]) - }) - .and_then(|p| p.finish(nwin))?; - let ctx = Arc::new(AndroidContext { egl_context, stopped: Some(Mutex::new(false)) }); - - let context = Context(ctx.clone()); - - Ok((win, context)) - } - - #[inline] - pub fn new_headless( - _el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); - let context = EglContext::new( - pf_reqs, - &gl_attr, - NativeDisplay::Android, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - )?; - let egl_context = context.finish_pbuffer(size)?; - let ctx = Arc::new(AndroidContext { egl_context, stopped: None }); - Ok(Context(ctx)) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - - self.0.egl_context.make_current() - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - - self.0.egl_context.make_not_current() - } - - #[inline] - pub fn resize(&self, _: u32, _: u32) {} - - #[inline] - pub fn is_current(&self) -> bool { - self.0.egl_context.is_current() - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.0.egl_context.get_proc_address(addr) - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - self.0.egl_context.buffer_age() - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - self.0.egl_context.swap_buffers() - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - self.0.egl_context.swap_buffers_with_damage(rects) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - self.0.egl_context.swap_buffers_with_damage_supported() - } - - #[inline] - pub fn get_api(&self) -> Api { - self.0.egl_context.get_api() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.0.egl_context.get_pixel_format() - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::EGLContext { - self.0.egl_context.raw_handle() - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> ffi::EGLDisplay { - self.0.egl_context.get_egl_display() - } -} +// pub type Display = EglDisplay; +// pub type Config = EglConfig; +// pub type NotCurrentContext = EglNotCurrentContext; +// pub type PossiblyCurrentContext = EglPossiblyCurrentContext; +// pub type Surface = EglSurface; diff --git a/glutin/src/platform_impl/ios/mod.rs b/glutin/src/platform_impl/ios/mod.rs deleted file mode 100644 index fddec561b3c..00000000000 --- a/glutin/src/platform_impl/ios/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![cfg(target_os = "ios")] - -pub use crate::api::ios::*; -pub use glutin_gles2_sys::id; diff --git a/glutin/src/platform_impl/linux/mod.rs b/glutin/src/platform_impl/linux/mod.rs new file mode 100644 index 00000000000..10485f908a0 --- /dev/null +++ b/glutin/src/platform_impl/linux/mod.rs @@ -0,0 +1,572 @@ +#![allow(unreachable_patterns)] + +use std::ffi::{self, CStr}; +use std::num::NonZeroU32; + +use raw_window_handle::RawDisplayHandle; + +#[rustfmt::skip] +#[cfg(feature = "egl")] +use crate::api::egl::{ + config::Config as EglConfig, + context::{ + NotCurrentContext as EglNotCurrentContext, + PossiblyCurrentContext as EglPossiblyCurrentContext, + }, + display::Display as EglDisplay, + surface::Surface as EglSurface, +}; + +#[rustfmt::skip] +#[cfg(feature = "glx")] +use crate::api::glx::{ + config::Config as GlxConfig, + context::{ + NotCurrentContext as GlxNotCurrentContext, + PossiblyCurrentContext as GlxPossiblyCurrentContext, + }, + display::Display as GlxDisplay, + surface::Surface as GlxSurface, +}; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig, +}; +use crate::context::{AsRawContext, ContextAttributes, RawContext}; +use crate::display::{AsRawDisplay, DisplayPicker, RawDisplay}; +use crate::error::Result; +use crate::surface::{ + AsRawSurface, DamageRect, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, + SurfaceTypeTrait, WindowSurface, +}; + +#[cfg(feature = "x11")] +pub mod x11; +#[cfg(feature = "x11")] +use x11::X11VisualInfo; + +pub enum Display { + #[cfg(feature = "egl")] + Egl(EglDisplay), + #[cfg(feature = "glx")] + Glx(GlxDisplay), +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + match self { + #[cfg(feature = "egl")] + Self::Egl(display) => display.raw_display(), + #[cfg(feature = "glx")] + Self::Glx(display) => display.raw_display(), + } + } +} + +impl Display { + #[cfg(all(feature = "egl", not(feature = "glx")))] + pub unsafe fn from_raw(display: RawDisplayHandle, _picker: DisplayPicker) -> Result { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } + + #[cfg(feature = "glx")] + pub unsafe fn from_raw(display: RawDisplayHandle, picker: DisplayPicker) -> Result { + use crate::display::DisplayApiPreference; + + #[cfg(feature = "egl")] + if picker.api_preference == DisplayApiPreference::Egl { + return Ok(Self::Egl(EglDisplay::from_raw(display)?)); + } + + #[cfg(feature = "glx")] + let registrator = picker + .glx_error_registrator + .expect("glx was requested, but error hook registrator wasn't provided."); + + #[cfg(feature = "glx")] + match picker.api_preference { + DisplayApiPreference::Glx => Ok(Self::Glx(GlxDisplay::from_raw(display, registrator)?)), + #[cfg(all(feature = "glx", feature = "egl"))] + DisplayApiPreference::EglThenGlx => { + if let Ok(display) = EglDisplay::from_raw(display) { + Ok(Self::Egl(display)) + } else { + Ok(Self::Glx(GlxDisplay::from_raw(display, registrator)?)) + } + } + #[cfg(all(feature = "glx", feature = "egl"))] + DisplayApiPreference::GlxThenEgl => { + if let Ok(display) = GlxDisplay::from_raw(display, registrator) { + Ok(Self::Glx(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } + } + _ => unreachable!(), + } + } + + /// Find configuration matching the given `template`. + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + match self { + #[cfg(feature = "egl")] + Self::Egl(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Egl))) + } + #[cfg(feature = "glx")] + Self::Glx(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Glx))) + } + } + } + + pub fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + match (self, config) { + #[cfg(feature = "egl")] + (Self::Egl(display), Config::Egl(config)) => { + Ok(NotCurrentContext::Egl(display.create_context(config, context_attributes)?)) + } + #[cfg(feature = "glx")] + (Self::Glx(display), Config::Glx(config)) => { + Ok(NotCurrentContext::Glx(display.create_context(config, context_attributes)?)) + } + _ => unreachable!(), + } + } + + pub fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + match (self, config) { + #[cfg(feature = "egl")] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?)) + } + #[cfg(feature = "glx")] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_window_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } + + pub fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + match (self, config) { + #[cfg(feature = "egl")] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)?)) + } + #[cfg(feature = "glx")] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pbuffer_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } + + pub fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + match (self, config) { + #[cfg(feature = "egl")] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?)) + } + #[cfg(feature = "glx")] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pixmap_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } +} + +pub enum Surface { + #[cfg(feature = "egl")] + Egl(EglSurface), + #[cfg(feature = "glx")] + Glx(GlxSurface), +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.raw_surface(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.raw_surface(), + } + } +} + +impl Surface { + pub fn buffer_age(&self) -> u32 { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.buffer_age(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.buffer_age(), + } + } + + pub fn width(&self) -> Option { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.width(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.width(), + } + } + + pub fn height(&self) -> Option { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.height(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.height(), + } + } + + pub fn is_single_buffered(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.is_single_buffered(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.is_single_buffered(), + } + } + + pub fn swap_buffers(&self) -> Result<()> { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.swap_buffers(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.swap_buffers(), + } + } + + pub fn swap_buffers_with_damage(&self, rects: &[DamageRect]) -> Result<()> { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.swap_buffers_with_damage(rects), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.swap_buffers_with_damage(rects), + } + } + + pub fn is_current(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.is_current(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.is_current(), + } + } + + pub fn is_current_draw(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.is_current_read(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.is_current_read(), + } + } + + pub fn is_current_read(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.is_current_read(), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.is_current_read(), + } + } +} + +impl Surface { + pub fn resize(&self, width: NonZeroU32, height: NonZeroU32) { + match self { + #[cfg(feature = "egl")] + Self::Egl(surface) => surface.resize(width, height), + #[cfg(feature = "glx")] + Self::Glx(surface) => surface.resize(width, height), + } + } +} + +pub enum NotCurrentContext { + #[cfg(feature = "egl")] + Egl(EglNotCurrentContext), + #[cfg(feature = "glx")] + Glx(GlxNotCurrentContext), +} + +impl NotCurrentContext { + pub fn make_current(self, surface: &Surface) -> PossiblyCurrentContext { + match (self, surface) { + #[cfg(feature = "egl")] + (Self::Egl(context), Surface::Egl(surface)) => { + PossiblyCurrentContext::Egl(context.make_current(surface)) + } + #[cfg(feature = "glx")] + (Self::Glx(context), Surface::Glx(surface)) => { + PossiblyCurrentContext::Glx(context.make_current(surface)) + } + _ => unreachable!(), + } + } + + pub fn make_current_draw_read( + self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> PossiblyCurrentContext { + match (self, surface_draw, surface_read) { + #[cfg(feature = "egl")] + (Self::Egl(context), Surface::Egl(surface_draw), Surface::Egl(surface_read)) => { + PossiblyCurrentContext::Egl( + context.make_current_draw_read(surface_draw, surface_read), + ) + } + #[cfg(feature = "glx")] + (Self::Glx(context), Surface::Glx(surface_draw), Surface::Glx(surface_read)) => { + PossiblyCurrentContext::Glx( + context.make_current_draw_read(surface_draw, surface_read), + ) + } + _ => unreachable!(), + } + } + + pub fn treat_as_current(self) -> PossiblyCurrentContext { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => PossiblyCurrentContext::Egl(context.treat_as_current()), + #[cfg(feature = "glx")] + Self::Glx(context) => PossiblyCurrentContext::Glx(context.treat_as_current()), + } + } +} + +pub enum PossiblyCurrentContext { + #[cfg(feature = "egl")] + Egl(EglPossiblyCurrentContext), + #[cfg(feature = "glx")] + Glx(GlxPossiblyCurrentContext), +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => context.raw_context(), + #[cfg(feature = "glx")] + Self::Glx(context) => context.raw_context(), + } + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => context.raw_context(), + #[cfg(feature = "glx")] + Self::Glx(context) => context.raw_context(), + } + } +} + +impl PossiblyCurrentContext { + pub fn make_current(&self, surface: &Surface) { + match (self, surface) { + #[cfg(feature = "egl")] + (Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface), + #[cfg(feature = "glx")] + (Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface), + _ => unreachable!(), + } + } + + pub fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + match (self, surface_draw, surface_read) { + #[cfg(feature = "egl")] + (Self::Egl(context), Surface::Egl(surface_draw), Surface::Egl(surface_read)) => { + context.make_current_draw_read(surface_draw, surface_read) + } + #[cfg(feature = "glx")] + (Self::Glx(context), Surface::Glx(surface_draw), Surface::Glx(surface_read)) => { + context.make_current_draw_read(surface_draw, surface_read) + } + _ => unreachable!(), + } + } + + pub fn make_not_current(self) -> NotCurrentContext { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => NotCurrentContext::Egl(context.make_not_current()), + #[cfg(feature = "glx")] + Self::Glx(context) => NotCurrentContext::Glx(context.make_not_current()), + } + } + + pub fn is_current(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => context.is_current(), + #[cfg(feature = "glx")] + Self::Glx(context) => context.is_current(), + } + } + + pub fn set_swap_interval(&self, interval: u16) { + unimplemented!() + } + + pub fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => context.get_proc_address(addr), + #[cfg(feature = "glx")] + Self::Glx(context) => context.get_proc_address(addr), + } + } + + pub fn update_after_resize(&self) { + match self { + #[cfg(feature = "egl")] + Self::Egl(context) => context.update_after_resize(), + #[cfg(feature = "glx")] + Self::Glx(context) => context.update_after_resize(), + } + } +} + +pub enum Config { + #[cfg(feature = "egl")] + Egl(EglConfig), + #[cfg(feature = "glx")] + Glx(GlxConfig), +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.raw_config(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.raw_config(), + } + } +} + +impl Config { + #[cfg(feature = "x11")] + pub fn x11_visual(&self) -> Option { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.x11_visual(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.x11_visual(), + } + } + pub fn color_buffer_type(&self) -> ColorBufferType { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.color_buffer_type(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.color_buffer_type(), + } + } + + pub fn native_visual(&self) -> u32 { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.native_visual(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.native_visual(), + } + } + + pub fn alpha_size(&self) -> u8 { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.alpha_size(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.alpha_size(), + } + } + + pub fn srgb_capable(&self) -> bool { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.srgb_capable(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.srgb_capable(), + } + } + + pub fn depth_size(&self) -> u8 { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.depth_size(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.depth_size(), + } + } + + pub fn stencil_size(&self) -> u8 { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.stencil_size(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.stencil_size(), + } + } + + pub fn sample_buffers(&self) -> u8 { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.sample_buffers(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.sample_buffers(), + } + } + + pub fn config_surface_types(&self) -> ConfigSurfaceTypes { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.config_surface_types(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.config_surface_types(), + } + } + + pub fn api(&self) -> Api { + match self { + #[cfg(feature = "egl")] + Self::Egl(config) => config.api(), + #[cfg(feature = "glx")] + Self::Glx(config) => config.api(), + } + } +} diff --git a/glutin/src/platform_impl/linux/x11.rs b/glutin/src/platform_impl/linux/x11.rs new file mode 100644 index 00000000000..05964bdedf0 --- /dev/null +++ b/glutin/src/platform_impl/linux/x11.rs @@ -0,0 +1,74 @@ +//! Dedicated utils to work with X11 shenanigans. + +use std::mem; + +use once_cell::sync::Lazy; +use x11_dl::xlib::{Display, XVisualInfo, Xlib}; +#[cfg(feature = "egl")] +use x11_dl::xlib::{VisualIDMask, XID}; +use x11_dl::xrender::Xrender; + +pub(crate) static XLIB: Lazy> = Lazy::new(|| Xlib::open().ok()); +static XRENDER: Lazy> = Lazy::new(|| Xrender::open().ok()); + +// The X11 visual info. +pub struct X11VisualInfo { + display: *mut Display, + raw: *const XVisualInfo, +} + +impl X11VisualInfo { + #[cfg(feature = "egl")] + pub(crate) unsafe fn from_xid(display: *mut Display, xid: XID) -> Option { + let xlib = XLIB.as_ref().unwrap(); + + if xid == 0 { + return None; + } + + let mut raw: XVisualInfo = std::mem::zeroed(); + raw.visualid = xid; + + let mut num_visuals = 0; + let raw = (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals); + + if raw.is_null() { + return None; + } + + Some(Self { display, raw }) + } + + pub(crate) unsafe fn from_raw(display: *mut Display, raw: *const XVisualInfo) -> Self { + Self { display, raw } + } + + /// Check wether the [`Self`] supports transparency. + pub fn supports_transparency(&self) -> bool { + let xrender = XRENDER.as_ref().unwrap(); + unsafe { + let visual_format = (xrender.XRenderFindVisualFormat)(self.display, (*self.raw).visual); + + (!visual_format.is_null()) + .then(|| (*visual_format).direct.alphaMask != 0) + .unwrap_or(false) + } + } + + /// Convert the visual to the raw pointer. + /// + /// You must clear it with `XFree` after the use. + pub fn into_raw(self) -> *const std::ffi::c_void { + let raw = self.raw as *const _; + mem::forget(self); + raw + } +} + +impl Drop for X11VisualInfo { + fn drop(&mut self) { + unsafe { + (XLIB.as_ref().unwrap().XFree)(self.raw as *mut _); + } + } +} diff --git a/glutin/src/platform_impl/macos/helpers.rs b/glutin/src/platform_impl/macos/helpers.rs deleted file mode 100644 index d247df4146f..00000000000 --- a/glutin/src/platform_impl/macos/helpers.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::{ - CreationError, GlAttributes, GlProfile, GlRequest, PixelFormatRequirements, ReleaseBehavior, -}; - -use cocoa::appkit::*; -use cocoa::base::nil; - -pub fn get_gl_profile( - opengl: &GlAttributes<&T>, - pf_reqs: &PixelFormatRequirements, -) -> Result { - let version = opengl.version.to_gl_version(); - // first, compatibility profile support is strict - if opengl.profile == Some(GlProfile::Compatibility) { - // Note: we are not using ranges because of a rust bug that should be - // fixed here: https://github.com/rust-lang/rust/pull/27050 - if version.unwrap_or((2, 1)) < (3, 2) { - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else if let Some(v) = version { - // second, process exact requested version, if any - if v < (3, 2) { - if opengl.profile.is_none() && v <= (2, 1) { - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else if v == (3, 2) { - Ok(NSOpenGLProfileVersion3_2Core) - } else { - Ok(NSOpenGLProfileVersion4_1Core) - } - } else if let GlRequest::Latest = opengl.version { - // now, find the latest supported version automatically; - let mut attributes: [u32; 6] = [0; 6]; - let mut current_idx = 0; - attributes[current_idx] = NSOpenGLPFAAllowOfflineRenderers as u32; - current_idx += 1; - - if let Some(true) = pf_reqs.hardware_accelerated { - attributes[current_idx] = NSOpenGLPFAAccelerated as u32; - current_idx += 1; - } - - if pf_reqs.double_buffer != Some(false) { - attributes[current_idx] = NSOpenGLPFADoubleBuffer as u32; - current_idx += 1 - } - - attributes[current_idx] = NSOpenGLPFAOpenGLProfile as u32; - current_idx += 1; - - for &profile in &[NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersion3_2Core] { - attributes[current_idx] = profile as u32; - let id = unsafe { NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes) }; - if id != nil { - unsafe { msg_send![id, release] } - return Ok(profile); - } - } - // nothing else to do - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } -} - -pub fn build_nsattributes( - pf_reqs: &PixelFormatRequirements, - profile: NSOpenGLPFAOpenGLProfiles, -) -> Result, CreationError> { - // NOTE: OS X no longer has the concept of setting individual - // color component's bit size. Instead we can only specify the - // full color size and hope for the best. Another hiccup is that - // `NSOpenGLPFAColorSize` also includes `NSOpenGLPFAAlphaSize`, - // so we have to account for that as well. - let alpha_depth = pf_reqs.alpha_bits.unwrap_or(8); - let color_depth = pf_reqs.color_bits.unwrap_or(24) + alpha_depth; - - let mut attributes = vec![ - NSOpenGLPFAOpenGLProfile as u32, - profile as u32, - NSOpenGLPFAClosestPolicy as u32, - NSOpenGLPFAColorSize as u32, - color_depth as u32, - NSOpenGLPFAAlphaSize as u32, - alpha_depth as u32, - NSOpenGLPFADepthSize as u32, - pf_reqs.depth_bits.unwrap_or(24) as u32, - NSOpenGLPFAStencilSize as u32, - pf_reqs.stencil_bits.unwrap_or(8) as u32, - NSOpenGLPFAAllowOfflineRenderers as u32, - ]; - - if let Some(true) = pf_reqs.hardware_accelerated { - attributes.push(NSOpenGLPFAAccelerated as u32); - } - - // Note: according to Apple docs, not specifying `NSOpenGLPFADoubleBuffer` - // equals to requesting a single front buffer, in which case most of the GL - // renderers will show nothing, since they draw to GL_BACK. - if pf_reqs.double_buffer != Some(false) { - attributes.push(NSOpenGLPFADoubleBuffer as u32); - } - - if pf_reqs.release_behavior != ReleaseBehavior::Flush { - return Err(CreationError::NoAvailablePixelFormat); - } - - if pf_reqs.stereoscopy { - unimplemented!(); // TODO: - } - - if pf_reqs.float_color_buffer { - attributes.push(NSOpenGLPFAColorFloat as u32); - } - - if let Some(samples) = pf_reqs.multisampling { - attributes.push(NSOpenGLPFAMultisample as u32); - attributes.push(NSOpenGLPFASampleBuffers as u32); - attributes.push(1); - attributes.push(NSOpenGLPFASamples as u32); - attributes.push(samples as u32); - } - - // attribute list must be null terminated. - attributes.push(0); - - Ok(attributes) -} diff --git a/glutin/src/platform_impl/macos/mod.rs b/glutin/src/platform_impl/macos/mod.rs deleted file mode 100644 index 10ae4bd98ec..00000000000 --- a/glutin/src/platform_impl/macos/mod.rs +++ /dev/null @@ -1,357 +0,0 @@ -#![cfg(target_os = "macos")] -use crate::{ - ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect, - Robustness, -}; - -use cgl::{kCGLCECrashOnRemovedFunctions, kCGLCPSurfaceOpacity, CGLEnable, CGLSetParameter}; -use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat}; -use cocoa::base::{id, nil}; -use cocoa::foundation::NSAutoreleasePool; -use core_foundation::base::TCFType; -use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; -use core_foundation::string::CFString; -use objc::runtime::{BOOL, NO}; - -use crate::platform::macos::WindowExtMacOS; -use winit; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::Deref; -use std::os::raw; -use std::str::FromStr; - -mod helpers; - -#[derive(Debug)] -pub enum Context { - WindowedContext(WindowedContext), - HeadlessContext(HeadlessContext), -} - -#[derive(Debug)] -pub struct WindowedContext { - // NSOpenGLContext - context: IdRef, - pixel_format: PixelFormat, -} - -#[derive(Debug)] -pub struct HeadlessContext { - context: IdRef, -} - -impl Context { - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - let transparent = wb.window.transparent; - let win = wb.build(el)?; - - let share_ctx = gl_attr.sharing.map_or(nil, |c| *c.get_id()); - - match gl_attr.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - - let view = win.ns_view() as id; - - let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; - let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; - unsafe { - let pixel_format = - IdRef::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes)); - let pixel_format = match pixel_format.non_nil() { - None => return Err(CreationError::NoAvailablePixelFormat), - Some(pf) => pf, - }; - - let gl_context = IdRef::new( - NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixel_format, share_ctx), - ); - let gl_context = match gl_context.non_nil() { - Some(gl_context) => gl_context, - None => { - return Err(CreationError::NotSupported( - "could not open gl context".to_string(), - )); - } - }; - - let pixel_format = { - let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 { - let mut value = 0; - NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( - *pixel_format, - &mut value, - attrib, - NSOpenGLContext::currentVirtualScreen(*gl_context), - ); - value - }; - - PixelFormat { - hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0, - color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - - get_attr(appkit::NSOpenGLPFAAlphaSize)) - as u8, - alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8, - depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8, - stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8, - stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0, - double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0, - multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 { - Some(get_attr(appkit::NSOpenGLPFASamples) as u16) - } else { - None - }, - srgb: true, - } - }; - - gl_context.setView_(view); - let value = if gl_attr.vsync { 1 } else { 0 }; - gl_context.setValues_forParameter_( - &value, - appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval, - ); - - if transparent { - let mut opacity = 0; - CGLSetParameter( - gl_context.CGLContextObj() as *mut _, - kCGLCPSurfaceOpacity, - &mut opacity, - ); - } - - CGLEnable(gl_context.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions); - - let context = WindowedContext { context: gl_context, pixel_format }; - Ok((win, Context::WindowedContext(context))) - } - } - - #[inline] - pub fn new_headless( - _el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - _size: dpi::PhysicalSize, - ) -> Result { - let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; - let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; - let context = unsafe { - let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes); - if pixelformat == nil { - return Err(CreationError::OsError( - "Could not create the pixel format".to_string(), - )); - } - let context = - NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil); - if context == nil { - return Err(CreationError::OsError( - "Could not create the rendering context".to_string(), - )); - } - - IdRef::new(context) - }; - - let headless = HeadlessContext { context }; - - Ok(Context::HeadlessContext(headless)) - } - - pub fn resize(&self, _width: u32, _height: u32) { - match *self { - Context::WindowedContext(ref c) => unsafe { c.context.update() }, - _ => unreachable!(), - } - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - Context::WindowedContext(ref c) => { - let _: () = msg_send![*c.context, update]; - c.context.makeCurrentContext(); - } - Context::HeadlessContext(ref c) => { - let _: () = msg_send![*c.context, update]; - c.context.makeCurrentContext(); - } - } - Ok(()) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if self.is_current() { - match *self { - Context::WindowedContext(ref c) => { - let _: () = msg_send![*c.context, update]; - NSOpenGLContext::clearCurrentContext(nil); - } - Context::HeadlessContext(ref c) => { - let _: () = msg_send![*c.context, update]; - NSOpenGLContext::clearCurrentContext(nil); - } - } - } - Ok(()) - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { - let context = match *self { - Context::WindowedContext(ref c) => *c.context, - Context::HeadlessContext(ref c) => *c.context, - }; - - let pool = NSAutoreleasePool::new(nil); - let current = NSOpenGLContext::currentContext(nil); - let res = if current != nil { - let is_equal: BOOL = msg_send![current, isEqual: context]; - is_equal != NO - } else { - false - }; - let _: () = msg_send![pool, release]; - res - } - } - - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let symbol_name: CFString = FromStr::from_str(addr).unwrap(); - let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); - let framework = - unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) }; - let symbol = unsafe { - CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) - }; - symbol as *const _ - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - unsafe { - match *self { - Context::WindowedContext(ref c) => { - let pool = NSAutoreleasePool::new(nil); - c.context.flushBuffer(); - let _: () = msg_send![pool, release]; - } - Context::HeadlessContext(_) => unreachable!(), - } - } - Ok(()) - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - 0 - } - - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false - } - - #[inline] - pub fn get_api(&self) -> crate::Api { - crate::Api::OpenGl - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - Context::WindowedContext(ref c) => c.pixel_format.clone(), - Context::HeadlessContext(_) => unreachable!(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - match self { - Context::WindowedContext(c) => c.context.deref().CGLContextObj() as *mut _, - Context::HeadlessContext(c) => c.context.deref().CGLContextObj() as *mut _, - } - } - - #[inline] - fn get_id(&self) -> IdRef { - match self { - Context::WindowedContext(w) => w.context.clone(), - Context::HeadlessContext(h) => h.context.clone(), - } - } -} - -#[derive(Debug)] -struct IdRef(id); - -impl IdRef { - fn new(i: id) -> IdRef { - IdRef(i) - } - - #[allow(dead_code)] - fn retain(i: id) -> IdRef { - if i != nil { - let _: id = unsafe { msg_send![i, retain] }; - } - IdRef(i) - } - - fn non_nil(self) -> Option { - if self.0 == nil { - None - } else { - Some(self) - } - } -} - -impl Drop for IdRef { - fn drop(&mut self) { - if self.0 != nil { - let _: () = unsafe { msg_send![self.0, release] }; - } - } -} - -impl Deref for IdRef { - type Target = id; - fn deref<'a>(&'a self) -> &'a id { - &self.0 - } -} - -impl Clone for IdRef { - fn clone(&self) -> IdRef { - if self.0 != nil { - let _: id = unsafe { msg_send![self.0, retain] }; - } - IdRef(self.0) - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} diff --git a/glutin/src/platform_impl/mod.rs b/glutin/src/platform_impl/mod.rs deleted file mode 100644 index a99870e09fb..00000000000 --- a/glutin/src/platform_impl/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub use self::platform_impl::*; - -#[cfg(target_os = "windows")] -#[path = "windows/mod.rs"] -mod platform_impl; -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#[path = "unix/mod.rs"] -mod platform_impl; -#[cfg(target_os = "macos")] -#[path = "macos/mod.rs"] -mod platform_impl; -#[cfg(target_os = "android")] -#[path = "android/mod.rs"] -mod platform_impl; -#[cfg(target_os = "ios")] -#[path = "ios/mod.rs"] -mod platform_impl; diff --git a/glutin/src/platform_impl/unix/mod.rs b/glutin/src/platform_impl/unix/mod.rs deleted file mode 100644 index 8ed39a46081..00000000000 --- a/glutin/src/platform_impl/unix/mod.rs +++ /dev/null @@ -1,471 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -#[cfg(not(any(feature = "x11", feature = "wayland")))] -compile_error!("at least one of the 'x11' or 'wayland' features must be enabled"); - -mod wayland; -mod x11; - -#[cfg(feature = "x11")] -use self::x11::X11Context; -use crate::api::osmesa; -use crate::{ - Api, ContextCurrentState, ContextError, CreationError, GlAttributes, NotCurrent, PixelFormat, - PixelFormatRequirements, Rect, -}; -#[cfg(feature = "x11")] -pub use x11::utils as x11_utils; - -#[cfg(feature = "x11")] -use crate::platform::unix::x11::XConnection; -use crate::platform::unix::EventLoopWindowTargetExtUnix; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::marker::PhantomData; -use std::os::raw; -#[cfg(feature = "x11")] -use std::sync::Arc; - -/// Context handles available on Unix-like platforms. -#[derive(Clone, Debug)] -pub enum RawHandle { - /// Context handle for a glx context. - #[cfg(feature = "x11")] - Glx(glutin_glx_sys::GLXContext), - /// Context handle for a egl context. - Egl(glutin_egl_sys::EGLContext), -} - -#[derive(Debug)] -pub enum ContextType { - #[cfg(feature = "x11")] - X11, - #[cfg(feature = "wayland")] - Wayland, - OsMesa, -} - -#[derive(Debug)] -pub enum Context { - #[cfg(feature = "x11")] - X11(x11::Context), - #[cfg(feature = "wayland")] - Wayland(wayland::Context), - OsMesa(osmesa::OsMesaContext), -} - -impl Context { - fn is_compatible(c: &Option<&Context>, ct: ContextType) -> Result<(), CreationError> { - if let Some(c) = *c { - match ct { - ContextType::OsMesa => match *c { - Context::OsMesa(_) => Ok(()), - _ => { - let msg = "Cannot share an OSMesa context with a non-OSMesa context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } - }, - #[cfg(feature = "x11")] - ContextType::X11 => match *c { - Context::X11(_) => Ok(()), - _ => { - let msg = "Cannot share an X11 context with a non-X11 context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } - }, - #[cfg(feature = "wayland")] - ContextType::Wayland => match *c { - Context::Wayland(_) => Ok(()), - _ => { - let msg = "Cannot share a Wayland context with a non-Wayland context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } - }, - } - } else { - Ok(()) - } - } - - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - #[cfg(feature = "wayland")] - if el.is_wayland() { - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - return wayland::Context::new(wb, el, pf_reqs, &gl_attr) - .map(|(win, context)| (win, Context::Wayland(context))); - } - #[cfg(feature = "x11")] - if el.is_x11() { - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - return x11::Context::new(wb, el, pf_reqs, &gl_attr) - .map(|(win, context)| (win, Context::X11(context))); - } - panic!("glutin was not compiled with support for this display server") - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - Self::new_headless_impl(el, pf_reqs, gl_attr, Some(size)) - } - - pub fn new_headless_impl( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - #[cfg(feature = "wayland")] - if el.is_wayland() { - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - return wayland::Context::new_headless(&el, pf_reqs, &gl_attr, size) - .map(|ctx| Context::Wayland(ctx)); - } - #[cfg(feature = "x11")] - if el.is_x11() { - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - return x11::Context::new_headless(&el, pf_reqs, &gl_attr, size) - .map(|ctx| Context::X11(ctx)); - } - panic!("glutin was not compiled with support for this display server") - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.make_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.make_current(), - Context::OsMesa(ref ctx) => ctx.make_current(), - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.make_not_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.make_not_current(), - Context::OsMesa(ref ctx) => ctx.make_not_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.is_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.is_current(), - Context::OsMesa(ref ctx) => ctx.is_current(), - } - } - - #[inline] - pub fn get_api(&self) -> Api { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_api(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_api(), - Context::OsMesa(ref ctx) => ctx.get_api(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => match *ctx.raw_handle() { - X11Context::Glx(ref ctx) => RawHandle::Glx(ctx.raw_handle()), - X11Context::Egl(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - }, - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - Context::OsMesa(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - } - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_egl_display(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_egl_display(), - _ => None, - } - } - - #[inline] - pub fn resize(&self, width: u32, height: u32) { - #![allow(unused)] - match *self { - #[cfg(feature = "x11")] - Context::X11(_) => (), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.resize(width, height), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_proc_address(addr), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_proc_address(addr), - Context::OsMesa(ref ctx) => ctx.get_proc_address(addr), - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers(), - _ => unreachable!(), - } - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers_with_damage(rects), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers_with_damage(rects), - _ => unreachable!(), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.buffer_age(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.buffer_age(), - _ => unreachable!(), - } - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers_with_damage_supported(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers_with_damage_supported(), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_pixel_format(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_pixel_format(), - _ => unreachable!(), - } - } -} - -/// A unix-specific extension to the [`ContextBuilder`] which allows building -/// unix-specific headless contexts. -/// -/// [`ContextBuilder`]: ../../struct.ContextBuilder.html -pub trait HeadlessContextExt { - /// Builds an OsMesa context. - /// - /// Errors can occur if the OpenGL [`Context`] could not be created. This - /// generally happens because the underlying platform doesn't support a - /// requested feature. - /// - /// [`Context`]: struct.Context.html - fn build_osmesa( - self, - size: dpi::PhysicalSize, - ) -> Result, CreationError> - where - Self: Sized; - - /// Builds an EGL-surfaceless context. - /// - /// Errors can occur if the OpenGL [`Context`] could not be created. This - /// generally happens because the underlying platform doesn't support a - /// requested feature. - /// - /// [`Context`]: struct.Context.html - fn build_surfaceless( - self, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> HeadlessContextExt for crate::ContextBuilder<'a, T> { - #[inline] - fn build_osmesa( - self, - size: dpi::PhysicalSize, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::OsMesa)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::OsMesa(ref ctx) => ctx, - _ => unreachable!(), - }); - osmesa::OsMesaContext::new(&pf_reqs, &gl_attr, size) - .map(|context| Context::OsMesa(context)) - .map(|context| crate::Context { context, phantom: PhantomData }) - } - - #[inline] - fn build_surfaceless( - self, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::new_headless_impl(el, &pf_reqs, &gl_attr, None) - .map(|context| crate::Context { context, phantom: PhantomData }) - } -} - -/// A unix-specific extension for the [`ContextBuilder`] which allows -/// assembling [`RawContext`]s. -/// -/// [`RawContext`]: ../../type.RawContext.html -/// [`ContextBuilder`]: ../../struct.ContextBuilder.html -pub trait RawContextExt { - /// Creates a raw context on the provided surface. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The surface/display_ptr is destroyed before the context - #[cfg(feature = "wayland")] - unsafe fn build_raw_wayland_context( - self, - display_ptr: *const wayland::wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - ) -> Result, CreationError> - where - Self: Sized; - - /// Creates a raw context on the provided window. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The xwin is destroyed before the context - #[cfg(feature = "x11")] - unsafe fn build_raw_x11_context( - self, - xconn: Arc, - xwin: raw::c_ulong, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> RawContextExt for crate::ContextBuilder<'a, T> { - #[inline] - #[cfg(feature = "wayland")] - unsafe fn build_raw_wayland_context( - self, - display_ptr: *const wayland::wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - wayland::Context::new_raw_context(display_ptr, surface, width, height, &pf_reqs, &gl_attr) - .map(|context| Context::Wayland(context)) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) - } - - #[inline] - #[cfg(feature = "x11")] - unsafe fn build_raw_x11_context( - self, - xconn: Arc, - xwin: raw::c_ulong, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - x11::Context::new_raw_context(xconn, xwin, &pf_reqs, &gl_attr) - .map(|context| Context::X11(context)) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) - } -} diff --git a/glutin/src/platform_impl/unix/wayland.rs b/glutin/src/platform_impl/unix/wayland.rs deleted file mode 100644 index 45228e6ff00..00000000000 --- a/glutin/src/platform_impl/unix/wayland.rs +++ /dev/null @@ -1,195 +0,0 @@ -#![cfg(feature = "wayland")] - -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType}; -use crate::{ - ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect, -}; - -use crate::platform::unix::{EventLoopWindowTargetExtUnix, WindowExtUnix}; -use glutin_egl_sys as ffi; -pub use wayland_client::sys::client::wl_display; -use winit; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::Deref; -use std::os::raw; -use std::sync::Arc; - -pub struct EglSurface(Arc); - -impl std::fmt::Debug for EglSurface { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "EglSurface(...)") - } -} - -#[derive(Debug)] -pub enum Context { - Windowed(EglContext, EglSurface), - PBuffer(EglContext), - Surfaceless(EglContext), -} - -impl Deref for Context { - type Target = EglContext; - - fn deref(&self) -> &Self::Target { - match self { - Context::Windowed(ctx, _) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Surfaceless(ctx) => ctx, - } - } -} - -impl Context { - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - let gl_attr = gl_attr.clone().map_sharing(|c| &**c); - let display_ptr = el.wayland_display().unwrap() as *const _; - let native_display = NativeDisplay::Wayland(Some(display_ptr as *const _)); - if let Some(size) = size { - let context = EglContext::new( - pf_reqs, - &gl_attr, - native_display, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish_pbuffer(size))?; - let context = Context::PBuffer(context); - Ok(context) - } else { - // Surfaceless - let context = EglContext::new( - pf_reqs, - &gl_attr, - native_display, - EglSurfaceType::Surfaceless, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish_surfaceless())?; - let context = Context::Surfaceless(context); - Ok(context) - } - } - - #[inline] - pub fn new( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - let win = wb.build(el)?; - - let size = win.inner_size(); - let (width, height): (u32, u32) = size.into(); - - let display_ptr = win.wayland_display().unwrap() as *const _; - let surface = win.wayland_surface(); - let surface = match surface { - Some(s) => s, - None => { - return Err(CreationError::NotSupported("Wayland not found".to_string())); - } - }; - - let context = Self::new_raw_context(display_ptr, surface, width, height, pf_reqs, gl_attr)?; - Ok((win, context)) - } - - #[inline] - pub fn new_raw_context( - display_ptr: *const wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result { - let egl_surface = unsafe { - wayland_egl::WlEglSurface::new_from_raw(surface as *mut _, width as i32, height as i32) - }; - let context = { - let gl_attr = gl_attr.clone().map_sharing(|c| &**c); - let native_display = NativeDisplay::Wayland(Some(display_ptr as *const _)); - EglContext::new(pf_reqs, &gl_attr, native_display, EglSurfaceType::Window, |c, _| { - Ok(c[0]) - }) - .and_then(|p| p.finish(egl_surface.ptr() as *const _))? - }; - let context = Context::Windowed(context, EglSurface(Arc::new(egl_surface))); - Ok(context) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - (**self).make_current() - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - (**self).make_not_current() - } - - #[inline] - pub fn is_current(&self) -> bool { - (**self).is_current() - } - - #[inline] - pub fn get_api(&self) -> crate::Api { - (**self).get_api() - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::EGLContext { - (**self).raw_handle() - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - Some((**self).get_egl_display()) - } - - #[inline] - pub fn resize(&self, width: u32, height: u32) { - match self { - Context::Windowed(_, surface) => surface.0.resize(width as i32, height as i32, 0, 0), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - (**self).get_proc_address(addr) - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - (**self).swap_buffers() - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - (**self).swap_buffers_with_damage(rects) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - (**self).swap_buffers_with_damage_supported() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - (**self).get_pixel_format().clone() - } -} diff --git a/glutin/src/platform_impl/unix/x11.rs b/glutin/src/platform_impl/unix/x11.rs deleted file mode 100644 index 9e2ef8f887a..00000000000 --- a/glutin/src/platform_impl/unix/x11.rs +++ /dev/null @@ -1,674 +0,0 @@ -#![cfg(feature = "x11")] - -use crate::api::egl::{ - self, Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType, EGL, -}; -use crate::api::glx::{Context as GlxContext, GLX}; -use crate::platform::unix::x11::XConnection; -use crate::platform::unix::{EventLoopWindowTargetExtUnix, WindowBuilderExtUnix, WindowExtUnix}; -use crate::platform_impl::x11_utils; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, Rect, -}; - -use glutin_glx_sys as ffi; -use winit; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::{Deref, DerefMut}; -use std::os::raw; -use std::sync::Arc; - -pub mod utils; - -#[derive(Debug)] -struct NoX11Connection; - -impl std::error::Error for NoX11Connection {} - -impl std::fmt::Display for NoX11Connection { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("failed to get x11 connection") - } -} - -#[derive(Debug)] -pub enum X11Context { - Glx(GlxContext), - Egl(EglContext), -} - -#[derive(Debug)] -pub struct ContextInner { - context: X11Context, -} - -enum Prototype<'a> { - Glx(crate::api::glx::ContextPrototype<'a>), - Egl(crate::api::egl::ContextPrototype<'a>), -} - -#[derive(Debug)] -pub enum Context { - Surfaceless(ContextInner), - PBuffer(ContextInner), - Windowed(ContextInner), -} - -impl Deref for Context { - type Target = ContextInner; - - fn deref(&self) -> &Self::Target { - match self { - Context::Surfaceless(ctx) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Windowed(ctx) => ctx, - } - } -} - -impl DerefMut for Context { - fn deref_mut(&mut self) -> &mut ContextInner { - match self { - Context::Surfaceless(ctx) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Windowed(ctx) => ctx, - } - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -// FIXME: -// When using egl, all the configs will not support transparency, even if -// transparency does work with glx. -// -// https://bugs.freedesktop.org/show_bug.cgi?id=67676 -// I'm on a patch. -pub fn select_config( - xconn: &Arc, - transparent: Option, - pf_reqs: &PixelFormatRequirements, - config_ids: Vec, - mut convert_to_xvisualinfo: F, -) -> Result<(T, ffi::XVisualInfo), ()> -where - F: FnMut(&T) -> Option, -{ - use crate::platform_impl::x11_utils::Lacks; - let mut chosen_config_id = None; - let mut lacks_what = None; - - for config_id in config_ids { - let visual_infos = match convert_to_xvisualinfo(&config_id) { - Some(vi) => vi, - None => continue, - }; - - let this_lacks_what = x11_utils::examine_visual_info( - &xconn, - visual_infos, - transparent == Some(true), - pf_reqs.x11_visual_xid, - ); - - match (lacks_what, &this_lacks_what) { - (Some(Ok(())), _) => unreachable!(), - - // Found it. - (_, Ok(())) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - break; - } - - // Better have something than nothing. - (None, _) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - } - - // Stick with the earlier. - (Some(Err(Lacks::Transparency)), Err(Lacks::Transparency)) => (), - (Some(Err(_)), Err(Lacks::XID)) => (), - - // Lacking transparency is better than lacking the xid. - (Some(Err(Lacks::XID)), Err(Lacks::Transparency)) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - } - } - } - - match lacks_what { - Some(Ok(())) => (), - Some(Err(Lacks::Transparency)) => log::warn!( - "Glutin could not a find fb config with an alpha mask. Transparency may be broken." - ), - Some(Err(Lacks::XID)) => panic!(), - None => unreachable!(), - } - - chosen_config_id.ok_or(()) -} - -impl Context { - fn try_then_fallback(mut f: F) -> Result - where - F: FnMut(bool) -> Result, - { - match f(false) { - Ok(ok) => Ok(ok), - Err(err1) => match f(true) { - Ok(ok) => Ok(ok), - Err(err2) => Err(err1.append(err2)), - }, - } - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - Self::try_then_fallback(|fallback| { - Self::new_headless_impl(el, pf_reqs, gl_attr, size.clone(), fallback) - }) - } - - fn new_headless_impl( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - fallback: bool, - ) -> Result { - let xconn = match el.xlib_xconnection() { - Some(xconn) => xconn, - None => { - return Err(CreationError::NoBackendAvailable(Box::new(NoX11Connection))); - } - }; - - // Get the screen_id for the window being built. - let screen_id = unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }; - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - if let Some(size) = size { - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::PBuffer, - fallback, - fallback, - Some(false), - )?; - - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish_pbuffer(size)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish_pbuffer(size)?), - }; - - let context = Context::PBuffer(ContextInner { context }); - - Ok(context) - } else { - // Surfaceless - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Surfaceless, - !fallback, - fallback, - Some(false), - )?; - - // finish creating the OpenGL context - let context = match context { - // TODO: glx impl - // - // According to GLX_EXT_no_config_context - // > 2) Are no-config contexts constrained to those GL & ES - // > implementations which can support them? - // > - // > RESOLVED: Yes. ES2 + OES_surfaceless_context, ES 3.0, and - // > GL 3.0 all support binding a context without a drawable. - // > This implies that they don't need to know drawable - // > attributes at context creation time. - // > - // > In principle, equivalent functionality could be possible - // > with ES 1.x + OES_surfaceless_context. This extension - // > makes no promises about that. An implementation wishing to - // > reliably support this combination, or a similarly - // > permissive combination for GL < 3.0, should indicate so - // > with an additional GLX extension. - - // Prototype::Glx(ctx) => - // X11Context::Glx(ctx.finish_surfaceless(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish_surfaceless()?), - _ => { - return Err(CreationError::NotSupported( - "Surfaceless GLX context not implemented".to_string(), - )) - } - }; - - let context = Context::Surfaceless(ContextInner { context }); - - Ok(context) - } - } - - #[inline] - fn new_first_stage<'a>( - xconn: &Arc, - pf_reqs: &PixelFormatRequirements, - gl_attr: &'a GlAttributes<&'a Context>, - screen_id: raw::c_int, - builder_glx_u: &'a mut Option>, - builder_egl_u: &'a mut Option>, - surface_type: EglSurfaceType, - prefer_egl: bool, - force_prefer_unless_only: bool, - transparent: Option, - ) -> Result, CreationError> { - let select_config = |cs, display| { - select_config(&xconn, transparent, pf_reqs, cs, |config_id| { - let xid = egl::get_native_visual_id(display, *config_id) as ffi::VisualID; - if xid == 0 { - return None; - } - Some(x11_utils::get_visual_info_from_xid(xconn, xid)) - }) - .map(|(c, _)| c) - }; - Ok(match gl_attr.version { - GlRequest::Latest - | GlRequest::Specific(Api::OpenGl, _) - | GlRequest::GlThenGles { .. } => { - // GLX should be preferred over EGL, otherwise crashes may occur - // on X11 – issue #314 - // - // However, with surfaceless, GLX isn't really there, so we - // should prefer EGL. - let glx = |builder_u: &'a mut Option<_>| { - let builder = gl_attr.clone(); - *builder_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Glx(ref c) => c, - _ => panic!("context already exists but is wrong type"), - })); - Ok(Prototype::Glx(GlxContext::new( - Arc::clone(&xconn), - pf_reqs, - builder_u.as_ref().unwrap(), - screen_id, - surface_type, - transparent, - )?)) - }; - - let egl = |builder_u: &'a mut Option<_>| { - let builder = gl_attr.clone(); - *builder_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Egl(ref c) => c, - _ => panic!("context already exists but is wrong type"), - })); - let native_display = NativeDisplay::X11(Some(xconn.display as *const _)); - Ok(Prototype::Egl(EglContext::new( - pf_reqs, - builder_u.as_ref().unwrap(), - native_display, - surface_type, - select_config, - )?)) - }; - - // if there is already a context, just use that. - // this prevents the "context already exists but is wrong type" panics above. - if let Some(c) = gl_attr.sharing { - match c.context { - X11Context::Glx(_) => { - GLX.as_ref().expect("found GLX context but GLX not loaded"); - return glx(builder_glx_u); - } - X11Context::Egl(_) => { - EGL.as_ref().expect("found EGL context but EGL not loaded"); - return egl(builder_egl_u); - } - } - } - - // force_prefer_unless_only does what it says on the tin, it - // forces only the prefered method to happen unless it's the - // only method available. - // - // Users of this function should first call with `prefer_egl` - // as ``, with - // `force_prefer_unless_only` as `false`. - // - // Then, if those users want to fallback and try the other - // method, they should call us with `prefer_egl` equal to - // `!` and `force_prefer_unless_only` - // as true. - // - // That way, they'll try their fallback if available, unless - // it was their only option and they have already tried it. - if !force_prefer_unless_only { - // If the preferred choice works, don't spend time testing - // if the other works. - if prefer_egl { - if let Some(_) = &*EGL { - return egl(builder_egl_u); - } else if let Some(_) = &*GLX { - return glx(builder_glx_u); - } - } else { - if let Some(_) = &*GLX { - return glx(builder_glx_u); - } else if let Some(_) = &*EGL { - return egl(builder_egl_u); - } - } - - return Err(CreationError::NotSupported( - "both libGL and libEGL are not present".to_string(), - )); - } else { - if prefer_egl { - if let Some(_) = &*EGL { - return egl(builder_egl_u); - } - } else { - if let Some(_) = &*GLX { - return glx(builder_glx_u); - } - } - - return Err(CreationError::NotSupported( - "lacking either libGL or libEGL so could not fallback to other".to_string(), - )); - } - } - GlRequest::Specific(Api::OpenGlEs, _) => { - if let Some(_) = *EGL { - let builder = gl_attr.clone(); - *builder_egl_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Egl(ref c) => c, - _ => panic!(), - })); - Prototype::Egl(EglContext::new( - pf_reqs, - builder_egl_u.as_ref().unwrap(), - NativeDisplay::X11(Some(xconn.display as *const _)), - surface_type, - select_config, - )?) - } else { - return Err(CreationError::NotSupported("libEGL not present".to_string())); - } - } - GlRequest::Specific(_, _) => { - return Err(CreationError::NotSupported( - "requested specific without gl or gles".to_string(), - )); - } - }) - } - - #[inline] - pub fn new( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - Self::try_then_fallback(|fallback| { - Self::new_impl(wb.clone(), el, pf_reqs, gl_attr, fallback) - }) - } - - fn new_impl( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - fallback: bool, - ) -> Result<(Window, Self), CreationError> { - let xconn = match el.xlib_xconnection() { - Some(xconn) => xconn, - None => { - return Err(CreationError::NoBackendAvailable(Box::new(NoX11Connection))); - } - }; - - // Get the screen_id for the window being built. - let screen_id = unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }; - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Window, - fallback, - fallback, - Some(wb.window.transparent), - )?; - - // getting the `visual_infos` (a struct that contains information about - // the visual to use) - let visual_infos = match context { - Prototype::Glx(ref p) => p.get_visual_infos().clone(), - Prototype::Egl(ref p) => { - utils::get_visual_info_from_xid(&xconn, p.get_native_visual_id() as ffi::VisualID) - } - }; - - let win = - wb.with_x11_visual(&visual_infos as *const _).with_x11_screen(screen_id).build(el)?; - - let xwin = win.xlib_window().unwrap(); - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish(xwin as _)?), - }; - - let context = Context::Windowed(ContextInner { context }); - - Ok((win, context)) - } - - #[inline] - pub fn new_raw_context( - xconn: Arc, - xwin: raw::c_ulong, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result { - Self::try_then_fallback(|fallback| { - Self::new_raw_context_impl(&xconn, xwin, pf_reqs, gl_attr, fallback) - }) - } - - fn new_raw_context_impl( - xconn: &Arc, - xwin: raw::c_ulong, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - fallback: bool, - ) -> Result { - let attrs = unsafe { - let mut attrs = ::std::mem::zeroed(); - (xconn.xlib.XGetWindowAttributes)(xconn.display, xwin, &mut attrs); - attrs - }; - - // Not particularly efficient, but it's the only method I can find. - let mut screen_id = 0; - unsafe { - while attrs.screen != (xconn.xlib.XScreenOfDisplay)(xconn.display, screen_id) { - screen_id += 1; - } - } - - let attrs = { - let mut attrs = unsafe { std::mem::zeroed() }; - unsafe { - (xconn.xlib.XGetWindowAttributes)(xconn.display, xwin, &mut attrs); - } - attrs - }; - - let visual_xid = unsafe { (xconn.xlib.XVisualIDFromVisual)(attrs.visual) }; - let mut pf_reqs = pf_reqs.clone(); - pf_reqs.x11_visual_xid = Some(visual_xid); - pf_reqs.depth_bits = Some(attrs.depth as _); - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - let context = Self::new_first_stage( - &xconn, - &pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Window, - fallback, - fallback, - None, - )?; - - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish(xwin as _)?), - }; - - let context = Context::Windowed(ContextInner { context }); - - Ok(context) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.make_current(), - X11Context::Egl(ref ctx) => ctx.make_current(), - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.make_not_current(), - X11Context::Egl(ref ctx) => ctx.make_not_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match self.context { - X11Context::Glx(ref ctx) => ctx.is_current(), - X11Context::Egl(ref ctx) => ctx.is_current(), - } - } - - #[inline] - pub fn get_api(&self) -> Api { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_api(), - X11Context::Egl(ref ctx) => ctx.get_api(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> &X11Context { - &self.context - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match self.context { - X11Context::Egl(ref ctx) => Some(ctx.get_egl_display()), - _ => None, - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_proc_address(addr), - X11Context::Egl(ref ctx) => ctx.get_proc_address(addr), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match self.context { - X11Context::Glx(ref ctx) => ctx.buffer_age(), - X11Context::Egl(ref ctx) => ctx.buffer_age(), - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.swap_buffers(), - X11Context::Egl(ref ctx) => ctx.swap_buffers(), - } - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(_) => { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - X11Context::Egl(ref ctx) => ctx.swap_buffers_with_damage(rects), - } - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - match self.context { - X11Context::Glx(_) => false, - X11Context::Egl(ref ctx) => ctx.swap_buffers_with_damage_supported(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_pixel_format(), - X11Context::Egl(ref ctx) => ctx.get_pixel_format(), - } - } -} diff --git a/glutin/src/platform_impl/unix/x11/utils.rs b/glutin/src/platform_impl/unix/x11/utils.rs deleted file mode 100644 index adc9bacffcc..00000000000 --- a/glutin/src/platform_impl/unix/x11/utils.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::platform::unix::x11::XConnection; -use glutin_glx_sys as ffi; - -use std::sync::Arc; - -pub fn get_visual_info_from_xid(xconn: &Arc, xid: ffi::VisualID) -> ffi::XVisualInfo { - assert_ne!(xid, 0); - let mut template: ffi::XVisualInfo = unsafe { std::mem::zeroed() }; - template.visualid = xid; - - let mut num_visuals = 0; - let vi = unsafe { - (xconn.xlib.XGetVisualInfo)( - xconn.display, - ffi::VisualIDMask, - &mut template, - &mut num_visuals, - ) - }; - xconn.check_errors().expect("Failed to call `XGetVisualInfo`"); - assert!(!vi.is_null()); - assert!(num_visuals == 1); - - let vi_copy = unsafe { std::ptr::read(vi as *const _) }; - unsafe { - (xconn.xlib.XFree)(vi as *mut _); - } - vi_copy -} - -#[derive(Clone, Copy, Debug)] -pub enum Lacks { - Transparency, - XID, -} - -/// Should always check for lack of xid before lack of transparency. -pub fn examine_visual_info( - xconn: &Arc, - visual_infos: ffi::XVisualInfo, - want_transparency: bool, - want_xid: Option, -) -> Result<(), Lacks> { - if let Some(want_xid) = want_xid { - if visual_infos.visualid != want_xid { - return Err(Lacks::XID); - } - } - - unsafe { - if want_transparency { - let pict_format = (xconn.xrender.XRenderFindVisualFormat)( - xconn.display as *mut _, - visual_infos.visual, - ); - if pict_format.is_null() { - return Err(Lacks::Transparency); - } - - if (*pict_format).direct.alphaMask == 0 { - return Err(Lacks::Transparency); - } - } - } - - return Ok(()); -} - -pub use super::select_config; -pub use crate::api::egl::SurfaceType; diff --git a/glutin/src/platform_impl/windows/mod.rs b/glutin/src/platform_impl/windows/mod.rs index e629441c1e5..cdb1d24520f 100644 --- a/glutin/src/platform_impl/windows/mod.rs +++ b/glutin/src/platform_impl/windows/mod.rs @@ -1,328 +1,342 @@ -#![cfg(target_os = "windows")] +use std::ffi::{self, CStr}; -use crate::{ - Api, ContextCurrentState, ContextError, CreationError, GlAttributes, GlRequest, NotCurrent, - PixelFormat, PixelFormatRequirements, Rect, +use crate::api::egl::{ + Config as EglConfig, Display as EglDisplay, NotCurrentContext as EglNotCurrentContext, + PossiblyCurrentContext as EglPossiblyCurrentContext, Surface as EglSurface, +}; +use crate::api::glx::{ + Config as WglConfig, Display as WglDisplay, NotCurrentContext as WglNotCurrentContext, + PossiblyCurrentContext as WglPossiblyCurrentContext, Surface as WglSurface, +}; +use crate::config::{Api, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate}; +use crate::context::ContextAttributes; +use crate::display::RawDisplay; +use crate::surface::{ + PbufferSurface, PixmapSurface, Rect, SurfaceAttributes, SurfaceTypeTrait, WindowSurface, }; -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType, EGL}; -use crate::api::wgl::Context as WglContext; -use crate::platform::windows::WindowExtWindows; - -use glutin_egl_sys as ffi; -use winapi::shared::windef::{HGLRC, HWND}; -use winit; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::platform::windows::WindowBuilderExtWindows; -use winit::window::{Window, WindowBuilder}; - -use std::marker::PhantomData; -use std::os::raw; - -/// Context handles available on Windows. -#[derive(Clone, Debug)] -pub enum RawHandle { - Egl(ffi::EGLContext), - Wgl(HGLRC), +pub enum Display { + Egl(EglDisplay), + Wgl(WglDisplay), } -#[derive(Debug)] -pub enum Context { - /// A regular window - Egl(EglContext), - Wgl(WglContext), - /// A regular window, but invisible. - HiddenWindowEgl(Window, EglContext), - HiddenWindowWgl(Window, WglContext), - /// An EGL pbuffer. - EglPbuffer(EglContext), -} +impl Display { + pub unsafe fn from_raw(display: RawDisplay) -> Self { + let display = EglDisplay::from_raw(display); + Self::Egl(display) + } -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Context { - /// See the docs in the crate root file. - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result<(Window, Self), CreationError> { - let win = wb.build(el)?; - let hwnd = win.hwnd() as HWND; - let ctx = Self::new_raw_context(hwnd, pf_reqs, gl_attr)?; - - Ok((win, ctx)) - } - - #[inline] - pub fn new_raw_context( - hwnd: HWND, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result { - match gl_attr.version { - GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => { - match (gl_attr.sharing, &*EGL) { - // We must use WGL. - (Some(&Context::HiddenWindowWgl(_, _)), _) - | (Some(&Context::Wgl(_)), _) - | (None, None) => { - let gl_attr_wgl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::HiddenWindowWgl(_, ref c) | Context::Wgl(ref c) => { - c.get_hglrc() - } - _ => unreachable!(), - }); - unsafe { WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) } - } - // We must use EGL. - (Some(_), Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Egl(ref c) - | Context::EglPbuffer(ref c) - | Context::HiddenWindowEgl(_, ref c) => c, - _ => unreachable!(), - }); - - EglContext::new( - &pf_reqs, - &gl_attr_egl, - NativeDisplay::Other(Some(std::ptr::null())), - EglSurfaceType::Window, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish(hwnd)) - .map(|c| Context::Egl(c)) - } - // Try EGL, fallback to WGL. - (None, Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|_| unreachable!()); - let gl_attr_wgl = gl_attr.clone().map_sharing(|_| unreachable!()); - - if let Ok(c) = EglContext::new( - &pf_reqs, - &gl_attr_egl, - NativeDisplay::Other(Some(std::ptr::null())), - EglSurfaceType::Window, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish(hwnd)) - { - Ok(Context::Egl(c)) - } else { - unsafe { - WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) - } - } - } - _ => panic!(), - } - } - _ => { - let gl_attr_wgl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::HiddenWindowWgl(_, ref c) | Context::Wgl(ref c) => c.get_hglrc(), - _ => panic!(), - }); - unsafe { WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) } + /// Find configuration matching the given `template`. + pub fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + match self { + Self::Egl(display) => Some(Box::new( + display.find_configs(template)?.into_iter().map(|config| Config::Egl(config)), + )), + Self::Wgl(display) => Some(Box::new( + display.find_configs(template)?.into_iter().map(|config| Config::Wgl(config)), + )), + } + } + + pub fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> NotCurrentContext { + match (self, config) { + (Self::Egl(display), Config::Egl(config)) => { + let context = display.create_context(config, context_attributes); + NotCurrentContext::Egl(context) } + (Self::Wgl(display), Config::Wgl(config)) => unimplemented!(), + _ => unreachable!(), } } - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - // if EGL is available, we try using EGL first - // if EGL returns an error, we try the hidden window method - match (gl_attr.sharing, &*EGL) { - (None, Some(_)) - | (Some(&Context::Egl(_)), Some(_)) - | (Some(&Context::HiddenWindowEgl(_, _)), Some(_)) - | (Some(&Context::EglPbuffer(_)), Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Egl(ref c) - | Context::EglPbuffer(ref c) - | Context::HiddenWindowEgl(_, ref c) => c, - _ => unreachable!(), - }); - - let native_display = NativeDisplay::Other(None); - let context = EglContext::new( - pf_reqs, - &gl_attr_egl, - native_display, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - ) - .and_then(|prototype| prototype.finish_pbuffer(size)) - .map(|ctx| Context::EglPbuffer(ctx)); + pub fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Surface { + match (self, config) { + (Self::Egl(display), Config::Egl(config)) => { + Surface::Egl(display.create_window_surface(config, surface_attributes)) + } + (Self::Wgl(display), Config::Wgl(config)) => unimplemented!(), + _ => unreachable!(), + } + } - if let Ok(context) = context { - return Ok(context); - } + pub fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Surface { + match (self, config) { + (Self::Egl(display), Config::Egl(config)) => { + Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)) } - _ => (), + (Self::Wgl(display), Config::Wgl(config)) => unimplemented!(), + _ => unreachable!(), } + } - let wb = WindowBuilder::new() - .with_visible(false) - .with_inner_size(size) - .with_drag_and_drop(false); - Self::new_windowed(wb, &el, pf_reqs, gl_attr).map(|(win, context)| match context { - Context::Egl(context) => Context::HiddenWindowEgl(win, context), - Context::Wgl(context) => Context::HiddenWindowWgl(win, context), + pub fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Surface { + match (self, config) { + (Self::Egl(display), Config::Egl(config)) => { + Surface::Egl(display.create_pixmap_surface(config, surface_attributes)) + } + (Self::Wgl(display), Config::Wgl(config)) => unimplemented!(), _ => unreachable!(), - }) + } + } +} + +pub enum Surface { + Egl(EglSurface), + Wgl(WglSurface), +} + +impl Surface { + pub fn buffer_age(&self) -> u32 { + match self { + Self::Egl(surface) => surface.buffer_age(), + Self::Wgl(surface) => unimplemented!(), + } } - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // Method is for API consistency. + pub fn width(&self) -> Option { + match self { + Self::Egl(surface) => surface.width(), + Self::Wgl(surface) => unimplemented!(), + } } - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.make_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.make_current(), + pub fn height(&self) -> Option { + match self { + Self::Egl(surface) => surface.height(), + Self::Wgl(surface) => unimplemented!(), } } - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.make_not_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.make_not_current(), + pub fn swap_buffers(&self) { + match self { + Self::Egl(surface) => surface.swap_buffers(), + Self::Wgl(surface) => unimplemented!(), } } - #[inline] pub fn is_current(&self) -> bool { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.is_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.is_current(), + match self { + Self::Egl(surface) => surface.is_current(), + Self::Wgl(surface) => unimplemented!(), } } - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_proc_address(addr), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_proc_address(addr), + pub fn is_current_draw(&self) -> bool { + match self { + Self::Egl(surface) => surface.is_current_read(), + Self::Wgl(surface) => unimplemented!(), } } - #[inline] - pub fn buffer_age(&self) -> u32 { - match *self { - Context::Egl(ref c) => c.buffer_age(), - _ => 0, + pub fn is_current_read(&self) -> bool { + match self { + Self::Egl(surface) => surface.is_current_read(), + Self::Wgl(surface) => unimplemented!(), } } - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) => c.swap_buffers(), - Context::Egl(ref c) => c.swap_buffers(), - _ => unreachable!(), + pub fn swap_buffers_with_damage(&self, damage_rects: &[Rect]) { + match self { + Self::Egl(surface) => unimplemented!(), + Self::Wgl(surface) => unimplemented!(), } } +} - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) +impl Surface { + pub fn resize(&self, width: u32, height: u32) { + match self { + Self::Egl(surface) => surface.resize(width, height), + Self::Wgl(surface) => unimplemented!(), + } } +} + +pub enum NotCurrentContext { + Egl(EglNotCurrentContext), + Wgl(WglNotCurrentContext), +} - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false +impl NotCurrentContext { + pub fn make_current(self, surface: &Surface) -> PossiblyCurrentContext { + match (self, surface) { + (Self::Egl(context), Surface::Egl(surface)) => { + PossiblyCurrentContext::Egl(context.make_current(&surface)) + } + (Self::Wgl(context), Surface::Wgl(surface)) => unimplemented!(), + _ => unreachable!(), + } } - #[inline] - pub fn get_api(&self) -> Api { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_api(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_api(), + pub fn make_current_draw_read( + self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> PossiblyCurrentContext { + match (self, surface_draw, surface_read) { + (Self::Egl(context), Surface::Egl(surface_draw), Surface::Egl(surface_read)) => { + PossiblyCurrentContext::Egl( + context.make_current_draw_read(&surface_draw, &surface_read), + ) + } + (Self::Wgl(context), Surface::Wgl(surface_draw), Surface::Wgl(surface_read)) => { + unimplemented!() + } + _ => unreachable!(), } } - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - Context::Wgl(ref c) => c.get_pixel_format(), - Context::Egl(ref c) => c.get_pixel_format(), + pub fn treat_as_current(self) -> PossiblyCurrentContext { + match self { + Self::Egl(context) => PossiblyCurrentContext::Egl(context.treat_as_current()), + Self::Wgl(context) => unimplemented!(), + } + } +} + +pub enum PossiblyCurrentContext { + Egl(EglPossiblyCurrentContext), + Wgl(WglPossiblyCurrentContext), +} + +impl PossiblyCurrentContext { + pub fn make_current(&self, surface: &Surface) { + match (self, surface) { + (Self::Egl(context), Surface::Egl(surface)) => context.make_current(&surface), + (Self::Wgl(context), Surface::Wgl(surface)) => unimplemented!(), _ => unreachable!(), } } - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { - RawHandle::Wgl(c.get_hglrc()) + pub fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) { + match (self, surface_draw, surface_read) { + (Self::Egl(context), Surface::Egl(surface_draw), Surface::Egl(surface_read)) => { + context.make_current_draw_read(&surface_draw, &surface_read) } - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()), + (Self::Wgl(context), Surface::Wgl(surface_draw), Surface::Wgl(surface_read)) => { + unimplemented!() + } + _ => unreachable!(), + } + } + + pub fn make_not_current(self) -> NotCurrentContext { + match self { + Self::Egl(context) => NotCurrentContext::Egl(context.make_not_current()), + Self::Wgl(context) => unimplemented!(), + _ => unreachable!(), + } + } + + pub fn is_current(&self) -> bool { + match self { + Self::Egl(context) => context.is_current(), + Self::Wgl(context) => unimplemented!(), } } - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match *self { - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => Some(c.get_egl_display()), - _ => None, + pub fn set_swap_interval(&self, interval: u16) { + unimplemented!() + } + + pub fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + match self { + Self::Egl(context) => context.get_proc_address(addr), + Self::Wgl(context) => unimplemented!(), + } + } + + pub fn update_after_resize(&self) { + match self { + Self::Egl(context) => context.update_after_resize(), + Self::Wgl(context) => unimplemented!(), + _ => unreachable!(), } } } -pub trait RawContextExt { - /// Creates a raw context on the provided window. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The window is destroyed before the context - unsafe fn build_raw_context( - self, - hwnd: *mut raw::c_void, - ) -> Result, CreationError> - where - Self: Sized; +pub enum Config { + Egl(EglConfig), + Wgl(WglConfig), } -impl<'a, T: ContextCurrentState> RawContextExt for crate::ContextBuilder<'a, T> { - #[inline] - unsafe fn build_raw_context( - self, - hwnd: *mut raw::c_void, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::new_raw_context(hwnd as *mut _, &pf_reqs, &gl_attr) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) +impl Config { + pub fn color_buffer_type(&self) -> ColorBufferType { + match self { + Self::Egl(config) => config.color_buffer_type(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn native_visual(&self) -> u32 { + match self { + Self::Egl(config) => config.native_visual(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn alpha_size(&self) -> u8 { + match self { + Self::Egl(config) => config.alpha_size(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn depth_size(&self) -> u8 { + match self { + Self::Egl(config) => config.depth_size(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn stencil_size(&self) -> u8 { + match self { + Self::Egl(config) => config.stencil_size(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn sample_buffers(&self) -> u8 { + match self { + Self::Egl(config) => config.sample_buffers(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn config_surface_types(&self) -> ConfigSurfaceTypes { + match self { + Self::Egl(config) => config.config_surface_types(), + Self::Wgl(config) => unimplemented!(), + } + } + + pub fn api(&self) -> Api { + match self { + Self::Egl(config) => config.api(), + Self::Wgl(config) => unimplemented!(), + } } } diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs new file mode 100644 index 00000000000..c4767ea1f08 --- /dev/null +++ b/glutin/src/surface.rs @@ -0,0 +1,284 @@ +//! A cross platform GL surface representation. + +use std::ffi; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +use raw_window_handle::RawWindowHandle; + +use crate::error::Result; +use crate::platform_impl::Surface as PlatformSurface; + +// Handle to the raw OpenGL surface. +#[derive(Debug, Clone, Copy)] +pub enum RawSurface { + // A pointer to EGLSurface. + #[cfg(feature = "egl")] + Egl(*const ffi::c_void), + + /// GLXDrawable. + #[cfg(feature = "glx")] + Glx(u64), +} + +pub trait AsRawSurface { + fn raw_surface(&self) -> RawSurface; +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + self.pimpl.raw_surface() + } +} + +// A platform native pixmap. +#[derive(Debug, Clone, Copy)] +pub enum NativePixmap { + /// XID of X11 pixmap. + X11Pixmap(u64), + + /// HBITMAP handle for windows bitmap. + WindowsPixmap(isize), +} + +impl NativePixmap { + pub fn raw(&self) -> *mut ffi::c_void { + match self { + Self::X11Pixmap(xid) => xid as *const _ as *mut _, + Self::WindowsPixmap(hbitmap) => hbitmap as *const _ as *mut _, + } + } +} + +// The underlying type of the surface. +#[derive(Debug, Clone, Copy)] +pub enum SurfaceType { + // The window surface. + Window, + + // Pixmap surface. + Pixmap, + + // Pbuffer surface. + Pbuffer, +} + +/// Marker that used to type-gate methods for window. +#[derive(Default, Debug, Clone, Copy)] +pub struct WindowSurface; + +/// Marker that used to type-gate methods for pbuffer. +#[derive(Default, Debug, Clone, Copy)] +pub struct PbufferSurface; + +/// Marker that used to type-gate methods for pixmap. +#[derive(Default, Debug, Clone, Copy)] +pub struct PixmapSurface; + +impl SurfaceTypeTrait for WindowSurface { + fn surface_type() -> SurfaceType { + SurfaceType::Window + } +} + +impl SurfaceTypeTrait for PbufferSurface { + fn surface_type() -> SurfaceType { + SurfaceType::Pbuffer + } +} + +impl SurfaceTypeTrait for PixmapSurface { + fn surface_type() -> SurfaceType { + SurfaceType::Pixmap + } +} + +pub trait SurfaceTypeTrait { + fn surface_type() -> SurfaceType; +} + +pub struct Surface { + pub(crate) pimpl: PlatformSurface, +} + +// The damage rect that is being used in [`Surface::swap_buffers_with_damage`]. The origin is in +// the bottom left of the surface. +#[derive(Debug, Clone, Copy, Default)] +pub struct DamageRect { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} + +impl DamageRect { + pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self { + Self { x, y, width, height } + } +} + +/// Attributes which are used for creating a paritcular surface. +#[derive(Default, Debug, Clone)] +pub struct SurfaceAttributes { + pub(crate) srgb: Option, + pub(crate) single_buffer: bool, + pub(crate) width: Option, + pub(crate) height: Option, + pub(crate) largest_pbuffer: bool, + pub(crate) raw_window_handle: Option, + pub(crate) native_pixmap: Option, + _ty: PhantomData, +} + +/// Builder to get the required set of attributes initialized before hand. +#[derive(Default)] +pub struct SurfaceAttributesBuilder { + attributes: SurfaceAttributes, +} + +impl SurfaceAttributesBuilder { + pub fn new() -> Self { + Default::default() + } + + /// Specify whether the surface should support srgb or not. Passing `None` means you don't care. + /// + /// # Api spicific. + /// + /// This only controls EGL surfaces, since the rest are using context for that. + pub fn with_srgb(mut self, srgb: Option) -> Self { + self.attributes.srgb = srgb; + self + } +} + +impl SurfaceAttributesBuilder { + pub fn build(mut self, native_pixmap: NativePixmap) -> SurfaceAttributes { + self.attributes.native_pixmap = Some(native_pixmap); + self.attributes + } +} + +impl SurfaceAttributesBuilder { + /// Specify whether the single buffer should be used instead of double buffering. This doesn't + /// guatantee that the resulted buffer will have only single buffer, to know that the single + /// buffer is actually used query the created surface with [`Surface::is_single_buffered`]. + /// + /// The surface is requested as double buffered by default. + /// + /// # Api spicific. + /// + /// This is EGL specific, since the rest are using it on the context. + pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { + self.attributes.single_buffer = single_buffer; + self + } + + pub fn build( + mut self, + raw_window_handle: RawWindowHandle, + width: NonZeroU32, + height: NonZeroU32, + ) -> SurfaceAttributes { + self.attributes.raw_window_handle = Some(raw_window_handle); + self.attributes.width = Some(width); + self.attributes.height = Some(height); + self.attributes + } +} + +impl SurfaceAttributesBuilder { + /// Request the largest pbuffer. + pub fn with_largest_pbuffer(mut self, largest_pbuffer: bool) -> Self { + self.attributes.largest_pbuffer = largest_pbuffer; + self + } + + /// The same as in [SurfaceAttributesBuilder::::with_single_buffer"]. + pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { + self.attributes.single_buffer = single_buffer; + self + } + + pub fn build( + mut self, + width: NonZeroU32, + height: NonZeroU32, + ) -> SurfaceAttributes { + self.attributes.width = Some(width); + self.attributes.height = Some(height); + self.attributes + } +} + +impl Surface { + pub(crate) fn from_platform(pimpl: PlatformSurface) -> Self { + Self { pimpl } + } + + /// The age of the back buffer of that surface. The `0` indicates that the buffer is either + /// a new one or we failed to get the information about its age. In both cases you must redraw + /// the entire buffer. + pub fn buffer_age(&self) -> u32 { + self.pimpl.buffer_age() + } + + /// The width of the underlying surface. + pub fn width(&self) -> Option { + self.pimpl.width() + } + + /// The height of the underlying surface. + pub fn height(&self) -> Option { + self.pimpl.height() + } + + /// Check whether the surface is single buffered. + pub fn is_single_buffered(&self) -> bool { + self.pimpl.is_single_buffered() + } + + /// Swaps the underlying back buffers when the surfate is not single buffered. + pub fn swap_buffers(&self) -> Result<()> { + self.pimpl.swap_buffers() + } + + /// Swaps the underlying back buffers when the surfate is not single buffered and pass the + /// [`DamageRect`] information to the system compositor. Providing empty slice will result in + /// damaging the entire surface. + /// + /// This Api doesn't do any parital rendering, it basically provides the hints for the system + /// compositor. + pub fn swap_buffers_with_damage(&self, damage_rects: &[DamageRect]) -> Result<()> { + self.pimpl.swap_buffers_with_damage(damage_rects) + } + + /// Check whether the surface is current on to the curren thread. + pub fn is_current(&self) -> bool { + self.pimpl.is_current() + } + + /// Check wherher the surface is current draw surface to the current thread. + pub fn is_current_draw(&self) -> bool { + self.pimpl.is_current_draw() + } + + /// Check wherher the surface is current read surface to the current thread. + pub fn is_current_read(&self) -> bool { + self.pimpl.is_current_read() + } +} + +impl Surface { + /// Resize the surface to the new size. + /// + /// This call is for compatibility reasons, on most platforms it's a no-op. + /// + /// # Platform specific + /// + /// **Wayland:** - resizes the surface. + /// **Other:** - no op. + pub fn resize(&self, width: NonZeroU32, height: NonZeroU32) { + self.pimpl.resize(width, height) + } +} diff --git a/glutin/src/windowed.rs b/glutin/src/windowed.rs deleted file mode 100644 index 48e9d7a31bf..00000000000 --- a/glutin/src/windowed.rs +++ /dev/null @@ -1,353 +0,0 @@ -use super::*; - -use std::marker::PhantomData; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -/// Represents an OpenGL [`Context`] and the [`Window`] with which it is -/// associated. -/// -/// Please see [`ContextWrapper`]. -/// -/// # Example -/// -/// ```no_run -/// # fn main() { -/// let mut el = glutin::event_loop::EventLoop::new(); -/// let wb = glutin::window::WindowBuilder::new(); -/// let windowed_context = glutin::ContextBuilder::new() -/// .build_windowed(wb, &el) -/// .unwrap(); -/// -/// let windowed_context = unsafe { windowed_context.make_current().unwrap() }; -/// # } -/// ``` -/// -/// [`ContextWrapper`]: struct.ContextWrapper.html -/// [`Window`]: struct.Window.html -/// [`Context`]: struct.Context.html -pub type WindowedContext = ContextWrapper; - -/// Represents an OpenGL [`Context`] which has an underlying window that is -/// stored separately. -/// -/// This type can only be created via one of three ways: -/// -/// * [`platform::unix::RawContextExt`] -/// * [`platform::windows::RawContextExt`] -/// * [`WindowedContext::split`] -/// -/// Please see [`ContextWrapper`]. -/// -/// [`ContextWrapper`]: struct.ContextWrapper.html -/// [`WindowedContext::split`]: type.WindowedContext.html#method.split -/// [`Context`]: struct.Context.html -#[cfg_attr( - target_os = "windows", - doc = "\ -[`platform::windows::RawContextExt`]: os/windows/enum.RawHandle.html -" -)] -#[cfg_attr( - not(target_os = "windows",), - doc = "\ -[`platform::windows::RawContextExt`]: os/index.html -" -)] -#[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\ -[`platform::unix::RawContextExt`]: os/index.html -" -)] -#[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`platform::unix::RawContextExt`]: os/unix/enum.RawHandle.html -" -)] -pub type RawContext = ContextWrapper; - -/// A context which has an underlying window, which may or may not be stored -/// separately. -/// -/// If the window is stored separately, it is a [`RawContext`]. Otherwise, -/// it is a [`WindowedContext`]. -/// -/// [`WindowedContext`]: type.WindowedContext.html -/// [`RawContext`]: type.RawContext.html -/// [`Context`]: struct.Context.html -#[derive(Debug)] -pub struct ContextWrapper { - pub(crate) context: Context, - pub(crate) window: W, -} - -impl WindowedContext { - /// Borrow the inner `W`. - pub fn window(&self) -> &Window { - &self.window - } - - /// Split the [`Window`] apart from the OpenGL [`Context`]. Should only be - /// used when intending to transfer the [`RawContext`] to another - /// thread. - /// - /// Unsaftey: - /// - The OpenGL [`Context`] must be dropped before the [`Window`]. - /// - /// [`RawContext`]: type.RawContext.html - /// [`Window`]: struct.Window.html - /// [`Context`]: struct.Context.html - pub unsafe fn split(self) -> (RawContext, Window) { - (RawContext { context: self.context, window: () }, self.window) - } -} - -impl ContextWrapper { - /// Swaps the buffers in case of double or triple buffering. - /// - /// You should call this function every time you have finished rendering, or - /// the image may not be displayed on the screen. - /// - /// **Warning**: if you enabled vsync, this function will block until the - /// next time the screen is refreshed. However drivers can choose to - /// override your vsync settings, which means that you can't know in - /// advance whether `swap_buffers` will block or not. - pub fn swap_buffers(&self) -> Result<(), ContextError> { - self.context.context.swap_buffers() - } - - /// Swaps the buffers in case of double or triple buffering using specified - /// damage rects. - /// - /// You should call this function every time you have finished rendering, or - /// the image may not be displayed on the screen. - /// - /// **Warning**: if you enabled vsync, this function will block until the - /// next time the screen is refreshed. However drivers can choose to - /// override your vsync settings, which means that you can't know in - /// advance whether `swap_buffers` will block or not. - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - self.context.context.swap_buffers_with_damage(rects) - } - - /// Returns whether or not swap_buffer_with_damage is available. If this - /// function returns false, any call to swap_buffers_with_damage will - /// return an error. - pub fn swap_buffers_with_damage_supported(&self) -> bool { - self.context.context.swap_buffers_with_damage_supported() - } - - /// Returns the pixel format of the main framebuffer of the context. - pub fn get_pixel_format(&self) -> PixelFormat { - self.context.context.get_pixel_format() - } - - /// Resize the context. - /// - /// Some platforms (macOS, Wayland) require being manually updated when - /// their window or surface is resized. - /// - /// The easiest way of doing this is to take every [`Resized`] window event - /// that is received and pass its [`PhysicalSize`] into this function. - /// - /// [`PhysicalSize`]: dpi/struct.PhysicalSize.html - /// [`Resized`]: event/enum.WindowEvent.html#variant.Resized - pub fn resize(&self, size: dpi::PhysicalSize) { - let (width, height) = size.into(); - self.context.context.resize(width, height); - } - - /// Query the underlying surface back's buffer age. - /// - /// Return `n` is the number of frames elapsed since it was most recently - /// drawn. - pub fn buffer_age(&self) -> u32 { - self.context.context.buffer_age() - } -} - -impl ContextWrapper { - /// Borrow the inner GL [`Context`]. - /// - /// [`Context`]: struct.Context.html - pub fn context(&self) -> &Context { - &self.context - } - - /// Sets this context as the current context. The previously current context - /// (if any) is no longer current. - /// - /// A failed call to `make_current` might make this, or no context - /// current. It could also keep the previous context current. What happens - /// varies by platform and error. - /// - /// To attempt to recover and get back into a know state, either: - /// - /// * attempt to use [`is_current`] to find the new current context; or - /// * call [`make_not_current`] on both the previously - /// current context and this context. - /// - /// # An higher level overview. - /// - /// In OpenGl, only a single context can be current in a thread at a time. - /// Making a new context current will make the old one not current. - /// Contexts can only be sent to different threads if they are not current. - /// - /// If you call `make_current` on some context, you should call - /// [`treat_as_not_current`] as soon as possible on the previously current - /// context. - /// - /// If you wish to move a currently current context to a different thread, - /// you should do one of two options: - /// - /// * Call `make_current` on another context, then call - /// [`treat_as_not_current`] on this context. - /// * Call [`make_not_current`] on this context. - /// - /// If you are aware of what context you intend to make current next, it is - /// preferable for performance reasons to call `make_current` on that - /// context, then [`treat_as_not_current`] on this context. - /// - /// If you are not aware of what context you intend to make current next, - /// consider waiting until you do. If you need this context not current - /// immediately (e.g. to transfer it to another thread), then call - /// [`make_not_current`] on this context. - /// - /// Please avoid calling [`make_not_current`] on one context only to call - /// `make_current` on another context before and/or after. This hurts - /// performance by requiring glutin to: - /// - /// * Check if this context is current; then - /// * If it is, change the current context from this context to none; then - /// * Change the current context from none to the new context. - /// - /// Instead prefer the method we mentioned above with `make_current` and - /// [`treat_as_not_current`]. - /// - /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current - /// [`treat_as_not_current`]: - /// struct.ContextWrapper.html#method.treat_as_not_current - /// [`is_current`]: struct.ContextWrapper.html#method.is_current - pub unsafe fn make_current( - self, - ) -> Result, (Self, ContextError)> { - let window = self.window; - match self.context.make_current() { - Ok(context) => Ok(ContextWrapper { window, context }), - Err((context, err)) => Err((ContextWrapper { window, context }, err)), - } - } - - /// If this context is current, makes this context not current. If this - /// context is not current however, this function does nothing. - /// - /// Please see [`make_current`]. - /// - /// [`make_current`]: struct.ContextWrapper.html#method.make_current - pub unsafe fn make_not_current( - self, - ) -> Result, (Self, ContextError)> { - let window = self.window; - match self.context.make_not_current() { - Ok(context) => Ok(ContextWrapper { window, context }), - Err((context, err)) => Err((ContextWrapper { window, context }, err)), - } - } - - /// Treats this context as not current, even if it is current. We do no - /// checks to confirm that this is actually case. - /// - /// If unsure whether or not this context is current, please use - /// [`make_not_current`] which will do nothing if this context is not - /// current. - /// - /// Please see [`make_current`]. - /// - /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current - /// [`make_current`]: struct.ContextWrapper.html#method.make_current - pub unsafe fn treat_as_not_current(self) -> ContextWrapper { - ContextWrapper { context: self.context.treat_as_not_current(), window: self.window } - } - - /// Treats this context as current, even if it is not current. We do no - /// checks to confirm that this is actually case. - /// - /// This function should only be used if you intend to track context - /// currency without the limited aid of glutin, and you wish to store - /// all the [`Context`]s as [`NotCurrent`]. - /// - /// Please see [`make_current`] for the prefered method of handling context - /// currency. - /// - /// [`make_current`]: struct.ContextWrapper.html#method.make_current - /// [`NotCurrent`]: enum.NotCurrent.html - /// [`Context`]: struct.Context.html - pub unsafe fn treat_as_current(self) -> ContextWrapper { - ContextWrapper { context: self.context.treat_as_current(), window: self.window } - } - - /// Returns true if this context is the current one in this thread. - pub fn is_current(&self) -> bool { - self.context.is_current() - } - - /// Returns the OpenGL API being used. - pub fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl ContextWrapper { - /// Returns the address of an OpenGL function. - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.context.get_proc_address(addr) - } -} - -impl std::ops::Deref for ContextWrapper { - type Target = Context; - fn deref(&self) -> &Self::Target { - &self.context - } -} - -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Builds the given window along with the associated GL context, returning - /// the pair as a [`WindowedContext`]. - /// - /// Errors can occur in two scenarios: - /// - If the window could not be created (via permission denied, - /// incompatible system, out of memory, etc.). This should be very rare. - /// - If the OpenGL [`Context`] could not be created. This generally - /// happens - /// because the underlying platform doesn't support a requested feature. - /// - /// [`WindowedContext`]: type.WindowedContext.html - /// [`Context`]: struct.Context.html - pub fn build_windowed( - self, - wb: WindowBuilder, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> { - let ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - platform_impl::Context::new_windowed(wb, el, &pf_reqs, &gl_attr).map(|(window, context)| { - WindowedContext { window, context: Context { context, phantom: PhantomData } } - }) - } -} diff --git a/glutin_egl_sys/src/lib.rs b/glutin_egl_sys/src/lib.rs index fac07d26f8d..ef8ce268713 100644 --- a/glutin_egl_sys/src/lib.rs +++ b/glutin_egl_sys/src/lib.rs @@ -8,6 +8,7 @@ target_os = "openbsd" ))] #![allow(non_camel_case_types)] +#![allow(clippy::all)] pub mod egl { pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; diff --git a/glutin_examples/Cargo.toml b/glutin_examples/Cargo.toml index cd588830007..67abc212452 100644 --- a/glutin_examples/Cargo.toml +++ b/glutin_examples/Cargo.toml @@ -12,8 +12,8 @@ publish = false [dependencies] glutin = { path = "../glutin" } -takeable-option = "0.4" -image = "0.21" +winit = { git = "https://github.com/rust-windowing/winit" } +raw-window-handle = "0.5.0" [build-dependencies] gl_generator = "0.14" diff --git a/glutin_examples/build.rs b/glutin_examples/build.rs index 77f6fa545e9..226c66ebdfe 100644 --- a/glutin_examples/build.rs +++ b/glutin_examples/build.rs @@ -1,4 +1,4 @@ -use gl_generator::{Api, Fallbacks, Profile, Registry}; +use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; use std::env; use std::fs::File; use std::path::PathBuf; @@ -9,7 +9,7 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); - Registry::new(Api::Gles2, (3, 3), Profile::Core, Fallbacks::All, []) - .write_bindings(gl_generator::StructGenerator, &mut file) + Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, []) + .write_bindings(GlobalGenerator, &mut file) .unwrap(); } diff --git a/glutin_examples/examples/buffer_age.rs b/glutin_examples/examples/buffer_age.rs deleted file mode 100644 index 5be0f01f396..00000000000 --- a/glutin_examples/examples/buffer_age.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); - - let windowed_context = ContextBuilder::new().with_vsync(true).build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(&windowed_context.context()); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - println!("Buffer age: {}", windowed_context.buffer_age()); - windowed_context.swap_buffers().unwrap(); - windowed_context.window().request_redraw(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/damage.rs b/glutin_examples/examples/damage.rs deleted file mode 100644 index dbad180b5fd..00000000000 --- a/glutin_examples/examples/damage.rs +++ /dev/null @@ -1,92 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use glutin::Rect; - -struct Color { - red: f32, - green: f32, - blue: f32, -} - -impl Color { - fn new() -> Color { - Color { red: 1.0, green: 0.5, blue: 0.0 } - } - fn next(&self) -> Color { - Color { - red: if self.red >= 1.0 { 0.0 } else { self.red + 0.01 }, - green: if self.green >= 1.0 { 0.0 } else { self.green + 0.01 }, - blue: if self.blue >= 1.0 { 0.0 } else { self.blue + 0.01 }, - } - } -} - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); - - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - if !windowed_context.swap_buffers_with_damage_supported() { - panic!("Damage not supported!"); - } - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(&windowed_context.context()); - - let mut color = Color::new(); - - gl.draw_frame([color.red, color.green, color.blue, 1.0]); - windowed_context.swap_buffers().unwrap(); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::CursorMoved { .. } => { - // Select a new color to render, draw and swap buffers. - // - // Note that damage is *intentionally* being misreported - // here to display the effect of damage. All changes must - // be covered by the reported damage, as the compositor is - // free to read more from the buffer than damage was - // reported, such as when windows unhide. - // - // However, here we only damage the lower left corner to - // show that it is (usually) only the damage that gets - // composited to screen. - // - // Panics if damage is not supported due to the unwrap. - color = color.next(); - gl.draw_frame([color.red, color.green, color.blue, 1.0]); - if windowed_context.swap_buffers_with_damage_supported() { - windowed_context - .swap_buffers_with_damage(&[Rect { - x: 0, - y: 0, - height: 100, - width: 100, - }]) - .unwrap(); - } else { - windowed_context.swap_buffers().unwrap(); - } - } - _ => (), - }, - _ => (), - } - }); -} diff --git a/glutin_examples/examples/fullscreen.rs b/glutin_examples/examples/fullscreen.rs deleted file mode 100644 index 9fe2290957a..00000000000 --- a/glutin_examples/examples/fullscreen.rs +++ /dev/null @@ -1,117 +0,0 @@ -mod support; - -use glutin::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::monitor::{MonitorHandle, VideoMode}; -use glutin::window::{Fullscreen, WindowBuilder}; -use std::io::{stdin, stdout, Write}; - -fn main() { - let el = EventLoop::new(); - - print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().ok().expect("Please enter a number"); - - let fullscreen = Some(match num { - 1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&el))), - 2 => Fullscreen::Borderless(Some(prompt_for_monitor(&el))), - _ => panic!("Please enter a valid number"), - }); - - println!("Press (F) to toggle fullscreen, (D) to toggle window decorations, and (M) to toggle maximized/minimized."); - - let mut is_maximized = false; - let mut decorations = true; - - let wb = WindowBuilder::new().with_title("Hello world!").with_fullscreen(fullscreen.clone()); - let windowed_context = glutin::ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - let gl = support::load(&windowed_context.context()); - - el.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(physical_size) => { - windowed_context.resize(physical_size); - } - WindowEvent::KeyboardInput { - input: KeyboardInput { virtual_keycode: Some(virtual_code), state, .. }, - .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, - (VirtualKeyCode::F, ElementState::Pressed) => { - if windowed_context.window().fullscreen().is_some() { - windowed_context.window().set_fullscreen(None); - } else { - windowed_context.window().set_fullscreen(fullscreen.clone()); - } - } - (VirtualKeyCode::S, ElementState::Pressed) => { - println!("window.fullscreen {:?}", windowed_context.window().fullscreen()); - } - (VirtualKeyCode::M, ElementState::Pressed) => { - is_maximized = !is_maximized; - windowed_context.window().set_maximized(is_maximized); - } - (VirtualKeyCode::D, ElementState::Pressed) => { - decorations = !decorations; - windowed_context.window().set_decorations(decorations); - } - _ => (), - }, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - windowed_context.swap_buffers().unwrap(); - } - _ => {} - } - }); -} - -// Enumerate monitors and prompt user to choose one -fn prompt_for_monitor(el: &EventLoop<()>) -> MonitorHandle { - for (num, monitor) in el.available_monitors().enumerate() { - println!("Monitor #{}: {:?}", num, monitor.name()); - } - - print!("Please write the number of the monitor to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().ok().expect("Please enter a number"); - let monitor = el.available_monitors().nth(num).expect("Please enter a valid ID"); - - println!("Using {:?}", monitor.name()); - - monitor -} - -fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode { - for (i, video_mode) in monitor.video_modes().enumerate() { - println!("Video mode #{}: {}", i, video_mode); - } - - print!("Please write the number of the video mode to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().ok().expect("Please enter a number"); - let video_mode = monitor.video_modes().nth(num).expect("Please enter a valid ID"); - - println!("Using {}", video_mode); - - video_mode -} diff --git a/glutin_examples/examples/headless.rs b/glutin_examples/examples/headless.rs deleted file mode 100644 index a6cb3836f03..00000000000 --- a/glutin_examples/examples/headless.rs +++ /dev/null @@ -1,151 +0,0 @@ -mod support; - -use glutin::dpi::PhysicalSize; -use glutin::event_loop::EventLoop; -use glutin::{ - Context, ContextBuilder, ContextCurrentState, CreationError, GlProfile, GlRequest, NotCurrent, -}; -use std::path::Path; -use support::gl; - -#[cfg(target_os = "linux")] -fn build_context_surfaceless( - cb: ContextBuilder, - el: &EventLoop<()>, -) -> Result, CreationError> { - use glutin::platform::unix::HeadlessContextExt; - cb.build_surfaceless(&el) -} - -fn build_context_headless( - cb: ContextBuilder, - el: &EventLoop<()>, -) -> Result, CreationError> { - let size_one = PhysicalSize::new(1, 1); - cb.build_headless(&el, size_one) -} - -#[cfg(target_os = "linux")] -fn build_context_osmesa( - cb: ContextBuilder, -) -> Result, CreationError> { - use glutin::platform::unix::HeadlessContextExt; - let size_one = PhysicalSize::new(1, 1); - cb.build_osmesa(size_one) -} - -#[cfg(target_os = "linux")] -fn build_context( - cb: ContextBuilder, -) -> Result<(Context, EventLoop<()>), [CreationError; 3]> { - // On unix operating systems, you should always try for surfaceless first, - // and if that does not work, headless (pbuffers), and if that too fails, - // finally osmesa. - // - // If willing, you could attempt to use hidden windows instead of os mesa, - // but note that you must handle events for the window that come on the - // events loop. - let el = EventLoop::new(); - - println!("Trying surfaceless"); - let err1 = match build_context_surfaceless(cb.clone(), &el) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - println!("Trying headless"); - let err2 = match build_context_headless(cb.clone(), &el) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - println!("Trying osmesa"); - let err3 = match build_context_osmesa(cb) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - Err([err1, err2, err3]) -} - -#[cfg(not(target_os = "linux"))] -fn build_context( - cb: ContextBuilder, -) -> Result<(Context, EventLoop<()>), CreationError> { - let el = EventLoop::new(); - build_context_headless(cb.clone(), &el).map(|ctx| (ctx, el)) -} - -fn main() { - let cb = ContextBuilder::new().with_gl_profile(GlProfile::Core).with_gl(GlRequest::Latest); - let size = PhysicalSize::new(768., 480.); - - let (headless_context, _el) = build_context(cb).unwrap(); - - let headless_context = unsafe { headless_context.make_current().unwrap() }; - - let gl = support::load(&headless_context); - - let mut fb = 0; - let mut render_buf = 0; - unsafe { - // Using the fb backing a pbuffer is very much a bad idea. Fails on - // many platforms, and is deprecated. Better just make your own fb. - // - // Surfaceless doesn't come with a surface, as the name implies, so - // you must make your own fb. - // - // Making an fb is not neccesary with osmesa, however, can't be bothered - // to have a different code path. - gl.gl.GenRenderbuffers(1, &mut render_buf); - gl.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - gl.gl.RenderbufferStorage(gl::RENDERBUFFER, gl::RGB8, size.width as _, size.height as _); - gl.gl.GenFramebuffers(1, &mut fb); - gl.gl.BindFramebuffer(gl::FRAMEBUFFER, fb); - gl.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - - gl.gl.Viewport(0, 0, size.width as _, size.height as _); - } - - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - - let mut pixels: Vec = vec![]; - pixels.resize(3 * size.width as usize * size.height as usize, 0); - unsafe { - gl.gl.ReadPixels( - 0, - 0, - size.width as _, - size.height as _, - gl::RGB, - gl::UNSIGNED_BYTE, - pixels.as_mut_ptr() as *mut _, - ); - } - - let mut pixels_flipped: Vec = vec![]; - for v in (0..size.height as _).rev() { - let s = 3 * v as usize * size.width as usize; - let o = 3 * size.width as usize; - pixels_flipped.extend_from_slice(&pixels[s..(s + o)]); - } - - image::save_buffer( - &Path::new("headless.png"), - &pixels_flipped, - size.width as u32, - size.height as u32, - image::RGB(8), - ) - .unwrap(); - - unsafe { - gl.gl.DeleteFramebuffers(1, &fb); - gl.gl.DeleteRenderbuffers(1, &render_buf); - } -} diff --git a/glutin_examples/examples/multiwindow.rs b/glutin_examples/examples/multiwindow.rs deleted file mode 100644 index 05872cf375f..00000000000 --- a/glutin_examples/examples/multiwindow.rs +++ /dev/null @@ -1,65 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use support::{ContextCurrentWrapper, ContextTracker, ContextWrapper}; - -fn main() { - let el = EventLoop::new(); - let mut ct = ContextTracker::default(); - - let mut windows = std::collections::HashMap::new(); - for index in 0..3 { - let title = format!("Charming Window #{}", index + 1); - let wb = WindowBuilder::new().with_title(title); - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - let gl = support::load(&windowed_context.context()); - let window_id = windowed_context.window().id(); - let context_id = ct.insert(ContextCurrentWrapper::PossiblyCurrent( - ContextWrapper::Windowed(windowed_context), - )); - windows.insert(window_id, (context_id, gl, index)); - } - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, window_id } => match event { - WindowEvent::Resized(physical_size) => { - let windowed_context = ct.get_current(windows[&window_id].0).unwrap(); - let windowed_context = windowed_context.windowed(); - windowed_context.resize(physical_size); - } - WindowEvent::CloseRequested => { - if let Some((cid, _, _)) = windows.remove(&window_id) { - ct.remove(cid); - println!("Window with ID {:?} has been closed", window_id); - } - } - _ => (), - }, - Event::RedrawRequested(window_id) => { - let window = &windows[&window_id]; - - let mut color = [1.0, 0.5, 0.7, 1.0]; - color.swap(0, window.2 % 3); - - let windowed_context = ct.get_current(window.0).unwrap(); - - window.1.draw_frame(color); - windowed_context.windowed().swap_buffers().unwrap(); - } - _ => (), - } - - if windows.is_empty() { - *control_flow = ControlFlow::Exit - } else { - *control_flow = ControlFlow::Wait - } - }); -} diff --git a/glutin_examples/examples/raw_context.rs b/glutin_examples/examples/raw_context.rs deleted file mode 100644 index 81ee3d40cd5..00000000000 --- a/glutin_examples/examples/raw_context.rs +++ /dev/null @@ -1,130 +0,0 @@ -#[cfg(any(target_os = "linux", target_os = "windows"))] -mod support; - -fn main() { - #[cfg(not(any(target_os = "linux", target_os = "windows")))] - unimplemented!(); - #[cfg(any(target_os = "linux", target_os = "windows"))] - this_example::main(); -} - -#[cfg(any(target_os = "linux", target_os = "windows"))] -mod this_example { - use super::support; - use glutin::event::{Event, WindowEvent}; - use glutin::event_loop::{ControlFlow, EventLoop}; - use glutin::window::WindowBuilder; - use glutin::ContextBuilder; - use std::io::Write; - use takeable_option::Takeable; - - pub fn main() { - print!("Do you want transparency? (true/false) (default: true): "); - std::io::stdout().flush().unwrap(); - - let mut transparency = String::new(); - std::io::stdin().read_line(&mut transparency).unwrap(); - let transparency = transparency.trim().parse().unwrap_or_else(|_| { - println!("Unknown input, assumming true."); - true - }); - - let (raw_context, el) = { - let el = EventLoop::new(); - let mut wb = WindowBuilder::new().with_title("A fantastic window!"); - - if transparency { - wb = wb.with_decorations(false).with_transparent(true); - } - - #[cfg(target_os = "linux")] - unsafe { - use glutin::platform::unix::{ - EventLoopWindowTargetExtUnix, RawContextExt, WindowExtUnix, - }; - - if el.is_wayland() { - let win = wb.build(&el).unwrap(); - let size = win.inner_size(); - let (width, height): (u32, u32) = size.into(); - - let display_ptr = win.wayland_display().unwrap() as *const _; - let surface = win.wayland_surface().unwrap(); - - let raw_context = ContextBuilder::new() - .build_raw_wayland_context(display_ptr, surface, width, height) - .unwrap(); - - (raw_context, el) - } else { - if transparency { - unimplemented!( - r#" -Users should make sure that the window gets built with an x11 visual that -supports transparency. Winit does not currently do this by default for x11 -because it is not provided with enough details to make a good choice. Normally -glutin decides this for winit, but this is not the case for raw contexts. - -Depending on the default order of the x11 visuals, transparency may by sheer -luck work for you. - -Such a task of selecting the appropriate x11 visual is outside the limited -scope of the glutin examples. Implementing it would likely require a lot of -platform specific egl/glx/x11 calls or exposing a lot of glutin's internals. -File a PR if you are interested in implementing the latter. - "# - ) - } - - let win = wb.build(&el).unwrap(); - let xconn = el.xlib_xconnection().unwrap(); - let xwindow = win.xlib_window().unwrap(); - let raw_context = - ContextBuilder::new().build_raw_x11_context(xconn, xwindow).unwrap(); - - (raw_context, el) - } - } - - #[cfg(target_os = "windows")] - unsafe { - let win = wb.build(&el).unwrap(); - use glutin::platform::windows::{RawContextExt, WindowExtWindows}; - - let hwnd = win.hwnd(); - let raw_context = ContextBuilder::new().build_raw_context(hwnd).unwrap(); - - (raw_context, el) - } - }; - - let raw_context = unsafe { raw_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", raw_context.get_pixel_format()); - - let gl = support::load(&*raw_context); - - let mut raw_context = Takeable::new(raw_context); - el.run(move |event, _, control_flow| { - println!("el {:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => { - Takeable::take(&mut raw_context); // Make sure it drops first - return; - } - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => raw_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame(if transparency { [0.0; 4] } else { [1.0, 0.5, 0.7, 1.0] }); - raw_context.swap_buffers().unwrap(); - } - _ => (), - } - }); - } -} diff --git a/glutin_examples/examples/sharing.rs b/glutin_examples/examples/sharing.rs deleted file mode 100644 index 8e8f43ee493..00000000000 --- a/glutin_examples/examples/sharing.rs +++ /dev/null @@ -1,150 +0,0 @@ -mod support; - -use glutin::dpi::PhysicalSize; -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use support::{gl, ContextCurrentWrapper, ContextTracker, ContextWrapper}; - -fn make_renderbuf(gl: &support::Gl, size: PhysicalSize) -> gl::types::GLuint { - let mut render_buf = 0; - unsafe { - gl.gl.GenRenderbuffers(1, &mut render_buf); - gl.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - gl.gl.RenderbufferStorage(gl::RENDERBUFFER, gl::RGB8, size.width as _, size.height as _); - } - - render_buf -} - -fn main() { - let el = EventLoop::new(); - let size = PhysicalSize::new(768, 480); - - let mut ct = ContextTracker::default(); - - let headless_context = - ContextBuilder::new().build_headless(&el, PhysicalSize::new(1, 1)).unwrap(); - - let wb = WindowBuilder::new().with_title("A fantastic window!").with_inner_size(size); - let windowed_context = - ContextBuilder::new().with_shared_lists(&headless_context).build_windowed(wb, &el).unwrap(); - - let headless_id = - ct.insert(ContextCurrentWrapper::NotCurrent(ContextWrapper::Headless(headless_context))); - let windowed_id = - ct.insert(ContextCurrentWrapper::NotCurrent(ContextWrapper::Windowed(windowed_context))); - - let windowed_context = ct.get_current(windowed_id).unwrap(); - println!( - "Pixel format of the window's GL context: {:?}", - windowed_context.windowed().get_pixel_format() - ); - let glw = support::load(&windowed_context.windowed().context()); - - let render_buf = make_renderbuf(&glw, size); - - let mut window_fb = 0; - unsafe { - glw.gl.GenFramebuffers(1, &mut window_fb); - // Both `GL_DRAW_FRAMEBUFFER` and `GL_READ_FRAMEBUFFER` need to be - // non-zero for `glFramebufferRenderbuffer`. We can change - // `GL_DRAW_FRAMEBUFFER` after. - glw.gl.BindFramebuffer(gl::FRAMEBUFFER, window_fb); - glw.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - glw.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); - glw.gl.Viewport(0, 0, size.width as _, size.height as _); - } - std::mem::drop(windowed_context); - - let headless_context = ct.get_current(headless_id).unwrap(); - let glc = support::load(&headless_context.headless()); - - let mut context_fb = 0; - unsafe { - // Using the fb backing a pbuffer is very much a bad idea. Fails on - // many platforms, and is deprecated. Better just make your own fb. - glc.gl.GenFramebuffers(1, &mut context_fb); - glc.gl.BindFramebuffer(gl::FRAMEBUFFER, context_fb); - glc.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - glc.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - glc.gl.Viewport(0, 0, size.width as _, size.height as _); - } - std::mem::drop(headless_context); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => { - unsafe { - let windowed_context = ct.get_current(windowed_id).unwrap(); - glw.gl.DeleteFramebuffers(1, &window_fb); - glw.gl.DeleteRenderbuffers(1, &render_buf); - std::mem::drop(windowed_context); - let _ = ct.get_current(headless_id).unwrap(); - glc.gl.DeleteFramebuffers(1, &context_fb); - } - return; - } - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => { - let windowed_context = ct.get_current(windowed_id).unwrap(); - windowed_context.windowed().resize(physical_size); - - unsafe { - windowed_context.windowed().swap_buffers().unwrap(); - glw.gl.RenderbufferStorage( - gl::RENDERBUFFER, - gl::RGB8, - size.width as _, - size.height as _, - ); - glw.gl.Viewport(0, 0, size.width as _, size.height as _); - std::mem::drop(windowed_context); - - let _ = ct.get_current(headless_id).unwrap(); - glc.gl.Viewport(0, 0, size.width as _, size.height as _); - } - } - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - let headless_context = ct.get_current(headless_id).unwrap(); - glc.draw_frame([1.0, 0.5, 0.7, 1.0]); - std::mem::drop(headless_context); - - let windowed_context = ct.get_current(windowed_id).unwrap(); - unsafe { - glw.gl.BlitFramebuffer( - 0, - 0, - size.width as _, - size.height as _, - 0, - 0, - size.width as _, - size.height as _, - gl::COLOR_BUFFER_BIT, - gl::NEAREST, - ); - } - windowed_context.windowed().swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/support/mod.rs b/glutin_examples/examples/support/mod.rs deleted file mode 100644 index 8afc689d105..00000000000 --- a/glutin_examples/examples/support/mod.rs +++ /dev/null @@ -1,355 +0,0 @@ -use glutin::{self, PossiblyCurrent}; - -use std::ffi::CStr; - -pub mod gl { - pub use self::Gles2 as Gl; - include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); -} - -pub struct Gl { - pub gl: gl::Gl, -} - -pub fn load(gl_context: &glutin::Context) -> Gl { - let gl = gl::Gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _); - - let version = unsafe { - let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - }; - - println!("OpenGL version {}", version); - - unsafe { - let vs = gl.CreateShader(gl::VERTEX_SHADER); - gl.ShaderSource(vs, 1, [VS_SRC.as_ptr() as *const _].as_ptr(), std::ptr::null()); - gl.CompileShader(vs); - - let fs = gl.CreateShader(gl::FRAGMENT_SHADER); - gl.ShaderSource(fs, 1, [FS_SRC.as_ptr() as *const _].as_ptr(), std::ptr::null()); - gl.CompileShader(fs); - - let program = gl.CreateProgram(); - gl.AttachShader(program, vs); - gl.AttachShader(program, fs); - gl.LinkProgram(program); - gl.UseProgram(program); - - let mut vb = std::mem::zeroed(); - gl.GenBuffers(1, &mut vb); - gl.BindBuffer(gl::ARRAY_BUFFER, vb); - gl.BufferData( - gl::ARRAY_BUFFER, - (VERTEX_DATA.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, - VERTEX_DATA.as_ptr() as *const _, - gl::STATIC_DRAW, - ); - - if gl.BindVertexArray.is_loaded() { - let mut vao = std::mem::zeroed(); - gl.GenVertexArrays(1, &mut vao); - gl.BindVertexArray(vao); - } - - let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _); - let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _); - gl.VertexAttribPointer( - pos_attrib as gl::types::GLuint, - 2, - gl::FLOAT, - 0, - 5 * std::mem::size_of::() as gl::types::GLsizei, - std::ptr::null(), - ); - gl.VertexAttribPointer( - color_attrib as gl::types::GLuint, - 3, - gl::FLOAT, - 0, - 5 * std::mem::size_of::() as gl::types::GLsizei, - (2 * std::mem::size_of::()) as *const () as *const _, - ); - gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint); - gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint); - } - - Gl { gl } -} - -impl Gl { - pub fn draw_frame(&self, color: [f32; 4]) { - unsafe { - self.gl.ClearColor(color[0], color[1], color[2], color[3]); - self.gl.Clear(gl::COLOR_BUFFER_BIT); - self.gl.DrawArrays(gl::TRIANGLES, 0, 3); - } - } -} - -#[rustfmt::skip] -static VERTEX_DATA: [f32; 15] = [ - -0.5, -0.5, 1.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 1.0, 0.0, - 0.5, -0.5, 0.0, 0.0, 1.0, -]; - -const VS_SRC: &'static [u8] = b" -#version 100 -precision mediump float; - -attribute vec2 position; -attribute vec3 color; - -varying vec3 v_color; - -void main() { - gl_Position = vec4(position, 0.0, 1.0); - v_color = color; -} -\0"; - -const FS_SRC: &'static [u8] = b" -#version 100 -precision mediump float; - -varying vec3 v_color; - -void main() { - gl_FragColor = vec4(v_color, 1.0); -} -\0"; - -pub use self::context_tracker::{ContextCurrentWrapper, ContextId, ContextTracker, ContextWrapper}; - -#[allow(dead_code)] // Not used by all examples -mod context_tracker { - use glutin::{ - self, Context, ContextCurrentState, ContextError, NotCurrent, PossiblyCurrent, - WindowedContext, - }; - use takeable_option::Takeable; - - pub enum ContextWrapper { - Headless(Context), - Windowed(WindowedContext), - } - - impl ContextWrapper { - pub fn headless(&mut self) -> &mut Context { - match self { - ContextWrapper::Headless(ref mut ctx) => ctx, - _ => panic!(), - } - } - - pub fn windowed(&mut self) -> &mut WindowedContext { - match self { - ContextWrapper::Windowed(ref mut ctx) => ctx, - _ => panic!(), - } - } - - fn map( - self, - fh: FH, - fw: FW, - ) -> Result, (Self, ContextError)> - where - FH: FnOnce(Context) -> Result, (Context, ContextError)>, - FW: FnOnce( - WindowedContext, - ) - -> Result, (WindowedContext, ContextError)>, - { - match self { - ContextWrapper::Headless(ctx) => match fh(ctx) { - Ok(ctx) => Ok(ContextWrapper::Headless(ctx)), - Err((ctx, err)) => Err((ContextWrapper::Headless(ctx), err)), - }, - ContextWrapper::Windowed(ctx) => match fw(ctx) { - Ok(ctx) => Ok(ContextWrapper::Windowed(ctx)), - Err((ctx, err)) => Err((ContextWrapper::Windowed(ctx), err)), - }, - } - } - } - - pub enum ContextCurrentWrapper { - PossiblyCurrent(ContextWrapper), - NotCurrent(ContextWrapper), - } - - impl ContextCurrentWrapper { - fn map_possibly(self, f: F) -> Result - where - F: FnOnce( - ContextWrapper, - ) -> Result< - ContextWrapper, - (ContextWrapper, ContextError), - >, - { - match self { - ret @ ContextCurrentWrapper::NotCurrent(_) => Ok(ret), - ContextCurrentWrapper::PossiblyCurrent(ctx) => match f(ctx) { - Ok(ctx) => Ok(ContextCurrentWrapper::NotCurrent(ctx)), - Err((ctx, err)) => Err((ContextCurrentWrapper::PossiblyCurrent(ctx), err)), - }, - } - } - - fn map_not(self, f: F) -> Result - where - F: FnOnce( - ContextWrapper, - ) -> Result< - ContextWrapper, - (ContextWrapper, ContextError), - >, - { - match self { - ret @ ContextCurrentWrapper::PossiblyCurrent(_) => Ok(ret), - ContextCurrentWrapper::NotCurrent(ctx) => match f(ctx) { - Ok(ctx) => Ok(ContextCurrentWrapper::PossiblyCurrent(ctx)), - Err((ctx, err)) => Err((ContextCurrentWrapper::NotCurrent(ctx), err)), - }, - } - } - } - - pub type ContextId = usize; - #[derive(Default)] - pub struct ContextTracker { - current: Option, - others: Vec<(ContextId, Takeable)>, - next_id: ContextId, - } - - impl ContextTracker { - pub fn insert(&mut self, ctx: ContextCurrentWrapper) -> ContextId { - let id = self.next_id; - self.next_id += 1; - - if let ContextCurrentWrapper::PossiblyCurrent(_) = ctx { - if let Some(old_current) = self.current { - unsafe { - self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| Ok(ctx.treat_as_not_current()), - |ctx| Ok(ctx.treat_as_not_current()), - ) - }) - }) - .unwrap() - } - } - self.current = Some(id); - } - - self.others.push((id, Takeable::new(ctx))); - id - } - - pub fn remove(&mut self, id: ContextId) -> ContextCurrentWrapper { - if Some(id) == self.current { - self.current.take(); - } - - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - Takeable::take(&mut self.others.remove(this_index).1) - } - - fn modify(&mut self, id: ContextId, f: F) -> Result<(), ContextError> - where - F: FnOnce( - ContextCurrentWrapper, - ) - -> Result, - { - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - - let this_context = Takeable::take(&mut self.others[this_index].1); - - match f(this_context) { - Err((ctx, err)) => { - self.others[this_index].1 = Takeable::new(ctx); - Err(err) - } - Ok(ctx) => { - self.others[this_index].1 = Takeable::new(ctx); - Ok(()) - } - } - } - - pub fn get_current( - &mut self, - id: ContextId, - ) -> Result<&mut ContextWrapper, ContextError> { - unsafe { - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - if Some(id) != self.current { - let old_current = self.current.take(); - - if let Err(err) = self.modify(id, |ctx| { - ctx.map_not(|ctx| { - ctx.map(|ctx| ctx.make_current(), |ctx| ctx.make_current()) - }) - }) { - // Oh noes, something went wrong - // Let's at least make sure that no context is current. - if let Some(old_current) = old_current { - if let Err(err2) = self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| ctx.make_not_current(), - |ctx| ctx.make_not_current(), - ) - }) - }) { - panic!( - "Could not `make_current` nor `make_not_current`, {:?}, {:?}", - err, err2 - ); - } - } - - if let Err(err2) = self.modify(id, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map(|ctx| ctx.make_not_current(), |ctx| ctx.make_not_current()) - }) - }) { - panic!( - "Could not `make_current` nor `make_not_current`, {:?}, {:?}", - err, err2 - ); - } - - return Err(err); - } - - self.current = Some(id); - - if let Some(old_current) = old_current { - self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| Ok(ctx.treat_as_not_current()), - |ctx| Ok(ctx.treat_as_not_current()), - ) - }) - }) - .unwrap(); - } - } - - match *self.others[this_index].1 { - ContextCurrentWrapper::PossiblyCurrent(ref mut ctx) => Ok(ctx), - ContextCurrentWrapper::NotCurrent(_) => panic!(), - } - } - } - } -} diff --git a/glutin_examples/examples/transparent.rs b/glutin_examples/examples/transparent.rs deleted file mode 100644 index 4c15b281817..00000000000 --- a/glutin_examples/examples/transparent.rs +++ /dev/null @@ -1,41 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new() - .with_title("A transparent window!") - .with_decorations(false) - .with_transparent(true); - - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(&windowed_context.context()); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([0.0; 4]); - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/window.rs b/glutin_examples/examples/window.rs index 44c92852fa2..9f1d97b8807 100644 --- a/glutin_examples/examples/window.rs +++ b/glutin_examples/examples/window.rs @@ -1,36 +1,103 @@ -mod support; +use std::ffi::CString; +use std::num::NonZeroU32; -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{ControlFlow, EventLoop}; +use winit::platform::unix::{self, WindowBuilderExtUnix}; +use winit::window::WindowBuilder; + +use glutin::config::{ConfigSurfaceTypes, ConfigTemplateBuilder}; +use glutin::context::ContextAttributesBuilder; +use glutin::display::{Display, DisplayApiPreference, DisplayPicker}; +use glutin::surface::{SurfaceAttributesBuilder, WindowSurface}; + +mod gl { + #![allow(clippy::all)] + include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); +} fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); + let event_loop = EventLoop::new(); + let raw_display = event_loop.raw_display_handle(); - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); + let picker = DisplayPicker::new() + .with_api_preference(DisplayApiPreference::GlxThenEgl) + .with_glx_error_registrator(Box::new(unix::register_xlib_error_hook)); - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + // Create connection to underlying OpenGL client Api. + let gl_display = unsafe { Display::from_raw(raw_display, picker).unwrap() }; - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); + // Create template to find OpenGL config. + let config_template = ConfigTemplateBuilder::new() + .with_alpha_size(8) + .with_transparency(false) + .with_surface_type(ConfigSurfaceTypes::WINDOW) + .build(); - let gl = support::load(&windowed_context.context()); + // Pick some OpenGL config. + let gl_config = &gl_display + .find_configs(config_template) + .unwrap() + .filter(|config| config.srgb_capable()) + .next() + .unwrap(); - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; + let mut window = WindowBuilder::new().with_transparent(true); + + // On X11 we must pass the visual we've got from the config. + if let Some(x11_visual) = gl_config.x11_visual() { + window = window.with_x11_visual(x11_visual.into_raw()); + } + + let window = window.build(&event_loop).unwrap(); + window.set_visible(true); + + let raw_window_handle = window.raw_window_handle(); + let (width, height): (u32, u32) = window.inner_size().into(); + let surface_attributes = SurfaceAttributesBuilder::::new().build( + raw_window_handle, + NonZeroU32::new(width).unwrap(), + NonZeroU32::new(height).unwrap(), + ); + let gl_surface = gl_display.create_window_surface(gl_config, &surface_attributes).unwrap(); + + let context_attributes = ContextAttributesBuilder::new().build(); + let gl_context = gl_display.create_context(&gl_config, &context_attributes).unwrap(); + + let gl_context = gl_context.make_current(&gl_surface); + + gl::load_with(|symbol| { + let symbol = CString::new(symbol).unwrap(); + gl_context.get_proc_address(symbol.as_c_str()) as *const _ + }); + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; match event { - Event::LoopDestroyed => return, Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + gl_surface.resize( + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + gl_context.update_after_resize(); + } + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } _ => (), }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - windowed_context.swap_buffers().unwrap(); + Event::RedrawEventsCleared => { + unsafe { + gl::ClearColor(1., 0., 1., 0.5); + gl::Clear(gl::COLOR_BUFFER_BIT); + window.request_redraw(); + } + + let _ = gl_surface.swap_buffers(); } _ => (), } diff --git a/glutin_examples/ios-example/rust/src/lib.rs b/glutin_examples/ios-example/rust/src/lib.rs index b273971e273..ff8327b7465 100644 --- a/glutin_examples/ios-example/rust/src/lib.rs +++ b/glutin_examples/ios-example/rust/src/lib.rs @@ -1,57 +1,4 @@ -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -#[path = "../../../examples/support/mod.rs"] -mod support; - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new() - .with_title("A transparent window!") - .with_decorations(false) - .with_transparent(true); - - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - let gl = support::load(&windowed_context.context()); - let mut inc: f32 = 0.0; - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::Touch(_touch) => { - const INCREMENTER: f32 = 0.05; - inc += INCREMENTER; - gl.draw_frame([ - inc % 1.0, - (inc + INCREMENTER) % 1.0, - (inc + INCREMENTER) % 1.0, - 0.0, - ]); - windowed_context.swap_buffers().unwrap(); - } - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([0.0; 4]); - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} - #[no_mangle] pub extern "C" fn run_app() { - main(); + // TODO } diff --git a/glutin_gles2_sys/src/lib.rs b/glutin_gles2_sys/src/lib.rs index 4a13fa65970..25701971406 100644 --- a/glutin_gles2_sys/src/lib.rs +++ b/glutin_gles2_sys/src/lib.rs @@ -1,5 +1,6 @@ #![cfg(target_os = "ios")] #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] +#![allow(clippy::all)] pub mod gles { include!(concat!(env!("OUT_DIR"), "/gles2_bindings.rs")); diff --git a/glutin_glx_sys/src/lib.rs b/glutin_glx_sys/src/lib.rs index fadb62a0fa0..58bf3d91947 100644 --- a/glutin_glx_sys/src/lib.rs +++ b/glutin_glx_sys/src/lib.rs @@ -5,6 +5,7 @@ target_os = "netbsd", target_os = "openbsd" ))] +#![allow(clippy::all)] pub use self::glx::types::GLXContext; pub use x11_dl::xlib::*; @@ -12,6 +13,22 @@ pub use x11_dl::xlib::*; /// GLX bindings pub mod glx { include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs")); + + // The GLX protocol error codes extracted from . + pub const PROTO_BAD_CONTEXT: types::GLenum = 0; + pub const PROTO_BAD_CONTEXT_STATE: types::GLenum = 1; + pub const PROTO_BAD_DRAWABLE: types::GLenum = 2; + pub const PROTO_BAD_PIXMAP: types::GLenum = 3; + pub const PROTO_BAD_CONTEXT_TAG: types::GLenum = 4; + pub const PROTO_BAD_CURRENT_WINDOW: types::GLenum = 5; + pub const PROTO_BAD_RENDER_REQUEST: types::GLenum = 6; + pub const PROTO_BAD_LARGE_REQUEST: types::GLenum = 7; + pub const PROTO_UNSUPPORTED_PRIVATE_REQUEST: types::GLenum = 8; + pub const PROTO_BAD_FBCONFIG: types::GLenum = 9; + pub const PROTO_BAD_PBUFFER: types::GLenum = 10; + pub const PROTO_BAD_CURRENT_DRAWABLE: types::GLenum = 11; + pub const PROTO_BAD_WINDOW: types::GLenum = 12; + pub const PROTO_BAD_PROFILE_ARB: types::GLenum = 13; } /// Functions that are not necessarily always available diff --git a/glutin_wgl_sys/src/lib.rs b/glutin_wgl_sys/src/lib.rs index 42cdd5688dc..0a8d2fdfcb7 100644 --- a/glutin_wgl_sys/src/lib.rs +++ b/glutin_wgl_sys/src/lib.rs @@ -1,4 +1,5 @@ #![cfg(any(target_os = "windows"))] +#![allow(clippy::all)] /// WGL bindings pub mod wgl { diff --git a/rustfmt.toml b/rustfmt.toml index 9e91dd035ec..f6630ecfb67 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,4 @@ use_small_heuristics = "Max" use_field_init_shorthand = true newline_style = "Unix" -edition = "2018" +edition = "2021"