From 2ade3a6c43f3e3dab75cf46f8c2ca785e56ab83c 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 | 68 +- glutin/build.rs | 24 + glutin/src/api/cgl/config.rs | 89 ++ glutin/src/api/cgl/context.rs | 183 +++ glutin/src/api/cgl/display.rs | 92 ++ glutin/src/api/cgl/mod.rs | 4 + glutin/src/api/cgl/surface.rs | 123 ++ glutin/src/api/dlloader.rs | 66 - glutin/src/api/egl/config.rs | 342 +++++ glutin/src/api/egl/context.rs | 305 +++++ glutin/src/api/egl/display.rs | 286 ++++ glutin/src/api/egl/make_current_guard.rs | 98 -- glutin/src/api/egl/mod.rs | 1311 +------------------ glutin/src/api/egl/surface.rs | 404 ++++++ glutin/src/api/glx/config.rs | 306 +++++ glutin/src/api/glx/context.rs | 249 ++++ glutin/src/api/glx/display.rs | 176 +++ glutin/src/api/glx/make_current_guard.rs | 78 -- glutin/src/api/glx/mod.rs | 849 ++---------- glutin/src/api/glx/surface.rs | 223 ++++ glutin/src/api/ios/mod.rs | 450 ------- glutin/src/api/mod.rs | 8 +- glutin/src/api/osmesa/mod.rs | 224 ---- glutin/src/api/wgl/config.rs | 89 ++ glutin/src/api/wgl/context.rs | 183 +++ glutin/src/api/wgl/display.rs | 92 ++ glutin/src/api/wgl/make_current_guard.rs | 54 - glutin/src/api/wgl/mod.rs | 902 +------------ glutin/src/api/wgl/surface.rs | 123 ++ glutin/src/config.rs | 438 +++++++ glutin/src/context.rs | 516 ++++++-- glutin/src/display.rs | 419 ++++++ glutin/src/error.rs | 164 +++ glutin/src/lib.rs | 707 +--------- 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/x11.rs | 81 ++ glutin/src/platform_impl/android/mod.rs | 164 --- glutin/src/platform_impl/ios/mod.rs | 4 - glutin/src/platform_impl/macos/helpers.rs | 134 -- glutin/src/platform_impl/macos/mod.rs | 358 ----- glutin/src/platform_impl/mod.rs | 23 - glutin/src/platform_impl/unix/mod.rs | 461 ------- glutin/src/platform_impl/unix/wayland.rs | 195 --- glutin/src/platform_impl/unix/x11.rs | 670 ---------- glutin/src/platform_impl/unix/x11/utils.rs | 70 - glutin/src/platform_impl/windows/mod.rs | 328 ----- glutin/src/prelude.rs | 17 + glutin/src/surface.rs | 327 +++++ glutin/src/windowed.rs | 323 ----- glutin_egl_sys/build.rs | 1 + glutin_egl_sys/src/lib.rs | 9 +- 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 | 129 -- glutin_examples/examples/sharing.rs | 142 -- glutin_examples/examples/support/mod.rs | 365 ------ glutin_examples/examples/transparent.rs | 41 - glutin_examples/examples/window.rs | 109 +- glutin_examples/ios-example/rust/src/lib.rs | 57 +- glutin_gles2_sys/src/lib.rs | 11 +- glutin_glx_sys/src/lib.rs | 24 +- glutin_wgl_sys/src/lib.rs | 3 +- rustfmt.toml | 2 +- 74 files changed, 5560 insertions(+), 8832 deletions(-) create mode 100644 glutin/build.rs create mode 100644 glutin/src/api/cgl/config.rs create mode 100644 glutin/src/api/cgl/context.rs create mode 100644 glutin/src/api/cgl/display.rs create mode 100644 glutin/src/api/cgl/mod.rs create mode 100644 glutin/src/api/cgl/surface.rs 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 create mode 100644 glutin/src/api/wgl/config.rs create mode 100644 glutin/src/api/wgl/context.rs create mode 100644 glutin/src/api/wgl/display.rs delete mode 100644 glutin/src/api/wgl/make_current_guard.rs create mode 100644 glutin/src/api/wgl/surface.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/unix.rs delete mode 100644 glutin/src/platform/windows.rs create mode 100644 glutin/src/platform/x11.rs delete mode 100644 glutin/src/platform_impl/android/mod.rs delete mode 100644 glutin/src/platform_impl/ios/mod.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 delete mode 100644 glutin/src/platform_impl/windows/mod.rs create mode 100644 glutin/src/prelude.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 626f76a26b..e08d520902 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -3,66 +3,42 @@ name = "glutin" version = "0.29.1" 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" +rust-version = "1.57.0" edition = "2021" -rust-version = "1.57" - -[package.metadata.docs.rs] -features = ["serde"] [features] -default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] -serde = ["winit/serde"] -x11 = ["winit/x11", "glutin_glx_sys"] -wayland = ["winit/wayland", "wayland-client", "wayland-egl"] -wayland-dlopen = ["winit/wayland-dlopen"] -wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] -wayland-csd-adwaita-notitle = ["winit/wayland-csd-adwaita-notitle"] +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-sys"] [dependencies] +libloading = "0.7.3" once_cell = "1.13" -winit = { version = "0.27.1", default-features = false } +raw-window-handle = "0.5.0" +bitflags = "1.3.2" + +[target.'cfg(target_os = "windows")'.dependencies] +glutin_egl_sys = { version = "0.1.6", 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.6", path = "../glutin_egl_sys" } -libloading = "0.7" -parking_lot = "0.12" -raw-window-handle = "0.5" - -[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.6", path = "../glutin_egl_sys" } -parking_lot = "0.12" - [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.4", features = ["dlopen"], optional = true } -wayland-egl = { version = "0.29.4", optional = true } -libloading = "0.7" -glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys" } +wayland-sys = { version = "0.30.0-beta.8", default-features = false, features = ["egl", "client"], optional = true } +x11-dl = { version = "2.20.0", optional = true } glutin_glx_sys = { version = "0.1.8", path = "../glutin_glx_sys", optional = true } -parking_lot = "0.12" -log = "0.4" +glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys", optional = true } +glutin_gles2_sys = { version = "0.1.5", path = "../glutin_gles2_sys", optional = true } + +[build-dependencies] +cfg_aliases = "0.1.1" diff --git a/glutin/build.rs b/glutin/build.rs new file mode 100644 index 0000000000..b542c07a9c --- /dev/null +++ b/glutin/build.rs @@ -0,0 +1,24 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // Setup alias to reduce `cfg` boilerplate. + cfg_aliases! { + // Systems. + android: { target_os = "android" }, + wasm: { target_arch = "wasm32" }, + macos: { target_os = "macos" }, + ios: { target_os = "ios" }, + apple: { any(target_os = "ios", target_os = "macos") }, + free_unix: { all(unix, not(apple), not(android)) }, + + // Native displays. + x11_platform: { all(feature = "x11", free_unix, not(wasm)) }, + wayland_platform: { all(feature = "wayland", free_unix, not(wasm)) }, + + // Backends. + egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm)) }, + glx_backend: { all(feature = "glx", x11_platform, not(wasm)) }, + wgl_backend: { all(feature = "wgl", windows, not(wasm)) }, + cgl_backend: { all(macos, not(wasm)) }, + } +} diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs new file mode 100644 index 0000000000..7ed8562ea7 --- /dev/null +++ b/glutin/src/api/cgl/config.rs @@ -0,0 +1,89 @@ +use std::os::raw::c_int; +use std::sync::Arc; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, +}; +use crate::display::GetGlDisplay; +use crate::private::Sealed; + +use super::display::Display; + +impl Display { + pub(crate) fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + todo!() + } +} + +#[derive(Clone)] +pub struct Config { + pub(crate) inner: Arc, +} + +pub(crate) struct ConfigInner {} + +impl Sealed for Config {} + +impl GlConfig for Config { + fn color_buffer_type(&self) -> ColorBufferType { + todo!() + } + + fn float_pixels(&self) -> bool { + todo!() + } + + fn native_visual(&self) -> u32 { + todo!() + } + + fn alpha_size(&self) -> u8 { + todo!() + } + + fn srgb_capable(&self) -> bool { + todo!() + } + + fn depth_size(&self) -> u8 { + todo!() + } + + fn stencil_size(&self) -> u8 { + todo!() + } + + fn sample_buffers(&self) -> u8 { + todo!() + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + todo!() + } + + fn api(&self) -> Api { + todo!() + } +} + +impl GetGlDisplay for Config { + type Target = Display; + fn display(&self) -> Self::Target { + todo!() + } +} + +impl Config { + fn raw_attribute(&self, attr: c_int) -> c_int { + todo!() + } +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + todo!() + } +} diff --git a/glutin/src/api/cgl/context.rs b/glutin/src/api/cgl/context.rs new file mode 100644 index 0000000000..06030e6166 --- /dev/null +++ b/glutin/src/api/cgl/context.rs @@ -0,0 +1,183 @@ +use std::cell::Cell; +use std::ffi::{self, CStr}; +use std::marker::PhantomData; +use std::rc::Rc; + +use crate::context::{AsRawContext, ContextAttributes, RawContext}; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::Result; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + todo!() + } +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread. + _nosendsync: PhantomData>, +} + +pub struct NotCurrentContext { + inner: ContextInner, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl Sealed for PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner, _nosync: PhantomData } + } +} + +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlConfig for NotCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl GetGlConfig for PossiblyCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + fn set_swap_interval(&self, interval: u16) {} + + fn is_current(&self) -> bool { + todo!() + } + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + todo!() + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type Surface = Surface; + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn make_current(self, surface: &Self::Surface) -> Result { + self.inner.make_current_draw_read(surface, surface)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } + + fn make_current_draw_read( + self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result { + self.inner.make_current_draw_read(surface_draw, surface_read)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + todo!() + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + todo!() + } +} + +struct ContextInner { + display: Display, + config: Config, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + todo!() + } + + fn make_not_current(&self) -> Result<()> { + todo!() + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + todo!() + } +} diff --git a/glutin/src/api/cgl/display.rs b/glutin/src/api/cgl/display.rs new file mode 100644 index 0000000000..8159db551e --- /dev/null +++ b/glutin/src/api/cgl/display.rs @@ -0,0 +1,92 @@ +use std::collections::HashSet; +use std::sync::Arc; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::Result; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; + +#[derive(Clone)] +pub struct Display { + pub(crate) inner: Arc, +} + +pub(crate) struct DisplayInner { + /// Client WGL extensions. + pub(crate) client_extensions: HashSet<&'static str>, +} + +impl Display { + /// Create WGL display. + /// + /// # Safety + /// + /// The `display` must point to the valid Windows display. + pub unsafe fn from_raw(display: RawDisplayHandle) -> Result { + todo!() + } +} + +impl Sealed for Display {} + +impl GlDisplay for Display { + type WindowSurface = Surface; + type PixmapSurface = Surface; + type PbufferSurface = Surface; + type Config = Config; + type NotCurrentContext = NotCurrentContext; + + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + // RawDisplay::Wgl(self.inner.raw.cast()) + todo!() + } +} diff --git a/glutin/src/api/cgl/mod.rs b/glutin/src/api/cgl/mod.rs new file mode 100644 index 0000000000..26e8206d80 --- /dev/null +++ b/glutin/src/api/cgl/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +pub mod context; +pub mod display; +pub mod surface; diff --git a/glutin/src/api/cgl/surface.rs b/glutin/src/api/cgl/surface.rs new file mode 100644 index 0000000000..a5164c0145 --- /dev/null +++ b/glutin/src/api/cgl/surface.rs @@ -0,0 +1,123 @@ +use std::marker::PhantomData; +use std::num::NonZeroU32; +use std::os::raw::{c_int, c_uint}; + +use raw_window_handle::RawWindowHandle; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::Result; +use crate::private::Sealed; +use crate::surface::{ + AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, + SurfaceType, SurfaceTypeTrait, WindowSurface, +}; + +use super::config::Config; +use super::display::Display; + +impl Display { + pub(crate) unsafe fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } + + pub(crate) fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } + + pub(crate) unsafe fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } +} + +pub struct Surface { + display: Display, + config: Config, + _ty: PhantomData, +} + +impl Sealed for Surface {} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + todo!() + } +} + +impl GlSurface for Surface { + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + todo!() + } + + fn width(&self) -> Option { + todo!() + } + + fn height(&self) -> Option { + todo!() + } + + fn is_single_buffered(&self) -> bool { + todo!() + } + + fn swap_buffers(&self) -> Result<()> { + todo!() + } + + fn is_current(&self) -> bool { + todo!() + } + + fn is_current_draw(&self) -> bool { + todo!() + } + + fn is_current_read(&self) -> bool { + todo!() + } + + fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { + // This isn't supported with GLXDrawable. + } +} + +impl GetGlConfig for Surface { + type Target = Config; + fn config(&self) -> Self::Target { + self.config.clone() + } +} + +impl GetGlDisplay for Surface { + type Target = Display; + fn display(&self) -> Self::Target { + self.display.clone() + } +} + +impl Surface { + fn raw_attribute(&self, attr: c_int) -> c_uint { + todo!() + } +} + +impl Drop for Surface { + fn drop(&mut self) { + todo!() + } +} diff --git a/glutin/src/api/dlloader.rs b/glutin/src/api/dlloader.rs deleted file mode 100644 index 40bbb414e2..0000000000 --- a/glutin/src/api/dlloader.rs +++ /dev/null @@ -1,66 +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 let Ok(lib) = lib { - return Ok(SymWrapper { inner: T::load_with(&lib), _lib: Arc::new(lib) }); - } - } - - 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 0000000000..57cdcec78a --- /dev/null +++ b/glutin/src/api/egl/config.rs @@ -0,0 +1,342 @@ +use std::mem; +use std::sync::Arc; + +use raw_window_handle::RawDisplayHandle; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::{EGLConfig, EGLint}; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigExtraSupport, ConfigSurfaceTypes, ConfigTemplate, + RawConfig, +}; +use crate::display::GetGlDisplay; +use crate::prelude::*; +use crate::private::Sealed; + +#[cfg(x11_platform)] +use crate::platform::x11::{X11GlConfigExt, X11VisualInfo}; + +use super::display::Display; + +impl Display { + pub(crate) 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); + } + }; + + let mut extra = ConfigExtraSupport::empty(); + if self.inner.client_extensions.contains("EGL_EXT_pixel_format_float") { + extra |= ConfigExtraSupport::FLOAT_PIXELS; + } + + if template.float_pixels && extra.contains(ConfigExtraSupport::FLOAT_PIXELS) { + config_attributes.push(egl::COLOR_COMPONENT_TYPE_EXT as EGLint); + config_attributes.push(egl::COLOR_COMPONENT_TYPE_FLOAT_EXT as EGLint); + } else if template.float_pixels { + return None; + } + + // 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.inner.egl.ChooseConfig( + self.inner.raw, + 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); + } + + if self.inner.client_extensions.contains("EGL_KHR_gl_colorspace") { + extra |= ConfigExtraSupport::SRGB; + } + + let configs = found_configs + .into_iter() + .map(move |raw| { + let inner = Arc::new(ConfigInner { display: self.clone(), raw, extra }); + Config { inner } + }) + .filter(move |_config| { + #[cfg(x11_platform)] + if template.transparency { + if let RawDisplayHandle::Xlib(display_handle) = self.inner.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.inner.egl.GetConfigs(self.inner.raw, std::ptr::null_mut(), 0, &mut num_configs); + num_configs as usize + } + } +} + +#[derive(Clone)] +pub struct Config { + pub(crate) inner: Arc, +} + +pub(crate) struct ConfigInner { + display: Display, + pub(crate) raw: EGLConfig, + extra: ConfigExtraSupport, +} + +impl GlConfig for Config { + 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!(), + } + } + + fn float_pixels(&self) -> bool { + if self.inner.extra.contains(ConfigExtraSupport::FLOAT_PIXELS) { + matches!( + self.raw_attribute(egl::COLOR_COMPONENT_TYPE_EXT as EGLint) as _, + egl::COLOR_COMPONENT_TYPE_FLOAT_EXT + ) + } else { + false + } + } + + fn native_visual(&self) -> u32 { + self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 + } + + fn alpha_size(&self) -> u8 { + self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 + } + + fn srgb_capable(&self) -> bool { + self.inner.extra.contains(ConfigExtraSupport::SRGB) + } + + fn depth_size(&self) -> u8 { + self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 + } + + fn stencil_size(&self) -> u8 { + self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 + } + + fn sample_buffers(&self) -> u8 { + self.raw_attribute(egl::SAMPLE_BUFFERS as EGLint) as u8 + } + + 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 + } + + 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 + } +} + +#[cfg(x11_platform)] +impl X11GlConfigExt for Config { + fn x11_visual(&self) -> Option { + match self.inner.display.inner.raw_display { + RawDisplayHandle::Xlib(display_handle) => unsafe { + let xid = self.native_visual(); + X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) + }, + _ => None, + } + } +} + +impl Sealed for Config {} + +impl GetGlDisplay for Config { + type Target = Display; + fn display(&self) -> Self::Target { + Display { inner: self.inner.display.inner.clone() } + } +} + +impl Config { + fn raw_attribute(&self, attr: EGLint) -> EGLint { + unsafe { + let mut val = 0; + self.inner.display.inner.egl.GetConfigAttrib( + self.inner.display.inner.raw, + self.inner.raw, + attr, + &mut val, + ); + val as EGLint + } + } +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + RawConfig::Egl(self.inner.raw) + } +} diff --git a/glutin/src/api/egl/context.rs b/glutin/src/api/egl/context.rs new file mode 100644 index 0000000000..b218b652cf --- /dev/null +++ b/glutin/src/api/egl/context.rs @@ -0,0 +1,305 @@ +use std::cell::Cell; +use std::ffi::{self, CStr}; +use std::marker::PhantomData; +use std::rc::Rc; + +use glutin_egl_sys::egl::types::EGLint; +use glutin_egl_sys::{egl, EGLContext}; + +use crate::config::{Api, GetGlConfig}; +use crate::context::{AsRawContext, ContextAttributes, GlProfile, RawContext, Robustness}; +use crate::display::GetGlDisplay; +use crate::error::{Error, ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) 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.inner.version.major <= 1 && self.inner.version.minor <= 3); + let supports_gles = !(self.inner.version.major <= 1 && self.inner.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()), + }; + + let is_one_five = !(self.inner.version.major <= 1 && self.inner.version.minor <= 4); + if is_one_five || self.inner.client_extensions.contains("EGL_KHR_create_context") { + let mut flags = 0; + + let has_robustsess = is_one_five + || self.inner.client_extensions.contains("EGL_EXT_create_context_robustness"); + let has_no_error = + self.inner.client_extensions.contains("EGL_KHR_create_context_no_error"); + + match context_attributes.robustness { + Robustness::NotRobust => (), + Robustness::NoError if has_no_error => { + attrs.push(egl::CONTEXT_OPENGL_NO_ERROR_KHR as EGLint); + attrs.push(egl::TRUE as EGLint); + } + Robustness::RobustLoseContextOnReset if has_robustsess => { + attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); + attrs.push(egl::LOSE_CONTEXT_ON_RESET as EGLint); + flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS; + } + Robustness::RobustNoResetNotification if has_robustsess => { + attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); + attrs.push(egl::NO_RESET_NOTIFICATION as EGLint); + flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS; + } + _ => { + return Err(Error::new( + None, + Some("Context robustness is not supported.".into()), + ErrorKind::NotSupported, + )) + } + } + + if context_attributes.debug && is_one_five && !has_no_error { + attrs.push(egl::CONTEXT_OPENGL_DEBUG as EGLint); + attrs.push(egl::TRUE as EGLint); + } + + if flags != 0 { + attrs.push(egl::CONTEXT_FLAGS_KHR as EGLint); + attrs.push(flags 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.inner.egl.BindAPI(api) == egl::FALSE { + return Err(super::check_error().err().unwrap()); + } + } + + unsafe { + let config = config.clone(); + let context = self.inner.egl.CreateContext( + self.inner.raw, + config.inner.raw, + shared_context, + attrs.as_ptr(), + ); + + if context == egl::NO_CONTEXT { + return Err(super::check_error().err().unwrap()); + } + + let inner = ContextInner { display: self.clone(), config, raw: context }; + Ok(NotCurrentContext::new(inner)) + } + } +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread, a. + _nosendsync: PhantomData>, +} + +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 } + } +} + +impl Sealed for PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + fn is_current(&self) -> bool { + unsafe { self.inner.display.inner.egl.GetCurrentContext() == self.inner.raw } + } + + fn set_swap_interval(&self, interval: u16) {} + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { self.inner.display.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> Self::PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type Surface = Surface; + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn make_current(self, surface: &Surface) -> Result { + self.inner.make_current_draw_read(surface, surface)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } + + fn make_current_draw_read( + self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result { + self.inner.make_current_draw_read(surface_draw, surface_read)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } +} + +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlConfig for NotCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl GetGlConfig for PossiblyCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Egl(self.inner.raw) + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Egl(self.inner.raw) + } +} + +struct ContextInner { + display: Display, + config: Config, + raw: EGLContext, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + unsafe { + let draw = surface_draw.raw; + let read = surface_read.raw; + if self.display.inner.egl.MakeCurrent(self.display.inner.raw, draw, read, self.raw) + == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + fn make_not_current(&self) -> Result<()> { + unsafe { + if self.display.inner.egl.MakeCurrent( + self.display.inner.raw, + egl::NO_SURFACE, + egl::NO_SURFACE, + egl::NO_CONTEXT, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + unsafe { + self.display.inner.egl.DestroyContext(self.display.inner.raw, self.raw); + } + } +} diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs new file mode 100644 index 0000000000..9c2953687f --- /dev/null +++ b/glutin/src/api/egl/display.rs @@ -0,0 +1,286 @@ +use std::collections::HashSet; +use std::ffi::CStr; +use std::sync::Arc; + +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::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; + +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. +#[derive(Clone)] +pub struct Display { + // Inner display to simplify passing it around. + pub(crate) inner: Arc, +} + +pub(crate) struct DisplayInner { + /// Pointer to the EGL handler to simplify API calls. + pub(crate) egl: &'static Egl, + + /// Pointer to the egl display. + pub(crate) raw: 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); + + let inner = + Arc::new(DisplayInner { egl, raw: display, raw_display, version, client_extensions }); + Ok(Self { inner }) + } + + 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 GlDisplay for Display { + type WindowSurface = Surface; + type PixmapSurface = Surface; + type PbufferSurface = Surface; + type Config = Config; + type NotCurrentContext = NotCurrentContext; + + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl Sealed for Display {} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Egl(self.inner.raw) + } +} + +/// 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 ae078bd8e4..0000000000 --- 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, Eq)] -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 49e8660125..8f5923422b 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -1,104 +1,71 @@ -#![cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -use std::ffi::{CStr, CString}; +use std::ffi::{self, CString}; use std::ops::{Deref, DerefMut}; -use std::os::raw; -use glutin_egl_sys as ffi; -use libloading; +use glutin_egl_sys::egl; + +use libloading::Library; +use once_cell::sync::{Lazy, OnceCell}; + #[cfg(unix)] use libloading::os::unix as libloading_os; #[cfg(windows)] use libloading::os::windows as libloading_os; -use once_cell::sync::{Lazy, OnceCell}; -#[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 self::make_current_guard::MakeCurrentGuard; -use crate::api::dlloader::{SymTrait, SymWrapper}; -#[cfg(not(target_os = "windows"))] -use crate::Rect; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; +use crate::error::{Error, ErrorKind, Result}; +use crate::lib_loading::{SymLoading, SymWrapper}; -#[derive(Clone)] -pub struct Egl(pub SymWrapper); +pub struct Egl(pub SymWrapper); + +pub mod config; +pub mod context; +pub mod display; +pub mod surface; -/// 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, ->; +pub static EGL: Lazy> = Lazy::new(|| { + #[cfg(windows)] + let paths = ["libEGL.dll", "atioglxx.dll"]; + + #[cfg(not(windows))] + let paths = ["libEGL.so.1", "libEGL.so"]; -static EGL_GET_PROC_ADDRESS: OnceCell = OnceCell::new(); + SymWrapper::new(&paths).map(Egl).ok() +}); -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 { - let s = std::ffi::CString::new(s.as_bytes()).unwrap(); - let s = s.as_bytes_with_nul(); +type EglGetProcAddress = unsafe extern "C" fn(*const ffi::c_void) -> *const ffi::c_void; +static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); - // Check if the symbol is available in the library directly. If - // it is, just return it. - if let Ok(sym) = unsafe { lib.get(s) } { - return *sym; +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 { + if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { + return *sym; + } } - let egl_get_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| 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\0").unwrap(); + 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() }); - // 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)(s.as_ptr().cast()) } + // 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) + } }; - 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(Egl) + Self::load_with(loader) } } -mod make_current_guard; - impl Deref for Egl { - type Target = ffi::egl::Egl; + type Target = egl::Egl; fn deref(&self) -> &Self::Target { &self.0 @@ -106,1180 +73,36 @@ impl Deref for Egl { } impl DerefMut for Egl { - fn deref_mut(&mut self) -> &mut ffi::egl::Egl { - &mut self.0 - } -} - -pub static EGL: Lazy> = Lazy::new(|| Egl::new().ok()); - -/// 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), -} - -#[derive(Debug)] -pub struct Context { - display: ffi::egl::types::EGLDisplay, - context: ffi::egl::types::EGLContext, - surface: Option>, - api: Api, - pixel_format: PixelFormat, -} - -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(); - - if egl.Initialize(display, &mut major, &mut minor) == 0 { - return Err(CreationError::OsError("eglInitialize failed".to_string())); - } - - Ok((major, minor)) - } -} - -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) && 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)) - } - } - } -} - -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_default(); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - - let has_dp_extension = |e: &str| dp_extensions.iter().any(|s| s == e); - - 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(), - ) - } - } - - 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 _); - 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(Clone, Copy, Debug, PartialEq, Eq)] -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 [`ContextPrototype::finish()`]. - #[allow(clippy::new_ret_no_self)] - 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_default(); - 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) - } - } - } 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) }; - - if ret == 0 { - match unsafe { egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => 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(&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 => 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 - } - } -} - -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(ContextError::OsError) - .unwrap(); - - guard.if_any_same_then_invalidate(surface, surface, self.context); - - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(!gl_finish_fn.is_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; - } - - // 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); - } - } -} - -#[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().any(|s| s == "EGL_KHR_surfaceless_context") { - 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 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(CreationError::OsError)?; - - 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(parking_lot::Mutex::new), - api: self.api, - pixel_format: self.pixel_format, - }) + 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, + _ => ErrorKind::Misc, }; - 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().any(|s| s == "EGL_KHR_create_context") { - 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().any(|s| s == "EGL_EXT_create_context_robustness"); - - match gl_robustness { - Robustness::NotRobust => (), - - Robustness::NoError => { - if extensions.iter().any(|s| s == "EGL_KHR_create_context_no_error") { - 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 |= 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 |= 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 |= 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 |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } - } - } - - if gl_debug && 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(Some(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 0000000000..cc9d2f1fa3 --- /dev/null +++ b/glutin/src/api/egl/surface.rs @@ -0,0 +1,404 @@ +// 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, EGLSurface, EGLint}; + +use raw_window_handle::RawWindowHandle; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{ + AsRawSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, SurfaceTypeTrait, + WindowSurface, +}; + +use super::config::Config; +use super::display::Display; + +const ATTR_SIZE_HINT: usize = 8; + +impl Display { + pub(crate) 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::::with_capacity(ATTR_SIZE_HINT); + + // 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 config = config.clone(); + let surface = Self::check_surface_error(self.inner.egl.CreatePbufferSurface( + self.inner.raw, + config.inner.raw, + attrs.as_ptr() as *const _, + ))?; + + Ok(Surface { + display: self.clone(), + native_window: None, + config, + raw: surface, + _ty: PhantomData, + }) + } + } + + pub(crate) unsafe 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::::with_capacity(ATTR_SIZE_HINT); + + if surface_attributes.srgb.is_some() + && self.inner.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); + + let config = config.clone(); + let surface = Self::check_surface_error(self.inner.egl.CreatePlatformPixmapSurface( + self.inner.raw, + config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ))?; + + Ok(Surface { + display: self.clone(), + config, + native_window: None, + raw: surface, + _ty: PhantomData, + }) + } + + pub(crate) unsafe 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::::with_capacity(ATTR_SIZE_HINT); + + // 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.inner.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); + + let config = config.clone(); + let surface = Self::check_surface_error(self.inner.egl.CreatePlatformWindowSurface( + self.inner.raw, + config.inner.raw, + native_window.raw(), + attrs.as_ptr() as *const _, + ))?; + + Ok(Surface { + display: self.clone(), + config, + native_window: Some(native_window), + raw: 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 { + display: Display, + config: Config, + pub(crate) raw: EGLSurface, + native_window: Option, + _ty: PhantomData, +} + +impl GlSurface for Surface { + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) as u32 + } + + fn width(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + fn height(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + fn is_single_buffered(&self) -> bool { + self.raw_attribute(egl::RENDER_BUFFER as EGLint) != egl::SINGLE_BUFFER as i32 + } + + fn swap_buffers(&self) -> Result<()> { + unsafe { + if self.display.inner.egl.SwapBuffers(self.display.inner.raw, self.raw) == egl::FALSE { + super::check_error() + } else { + Ok(()) + } + } + } + + fn is_current(&self) -> bool { + self.is_current_draw() && self.is_current_read() + } + + fn is_current_draw(&self) -> bool { + unsafe { self.display.inner.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.raw } + } + + fn is_current_read(&self) -> bool { + unsafe { self.display.inner.egl.GetCurrentSurface(egl::READ as EGLint) == self.raw } + } + + fn resize(&self, width: NonZeroU32, height: NonZeroU32) { + self.native_window.as_ref().unwrap().resize(width, height) + } +} + +impl Sealed for Surface {} + +impl GetGlDisplay for Surface { + type Target = Display; + fn display(&self) -> Self::Target { + self.display.clone() + } +} + +impl GetGlConfig for Surface { + type Target = Config; + fn config(&self) -> Self::Target { + self.config.clone() + } +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + RawSurface::Egl(self.raw) + } +} + +// The damage rect that is being used in [`Surface::swap_buffers_with_damage`]. The origin is in +// the bottom left of the surface. +#[repr(C)] +#[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 } + } +} + +impl Surface { + /// 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, rects: &[DamageRect]) -> Result<()> { + unsafe { + if self.display.inner.egl.SwapBuffersWithDamageKHR( + self.display.inner.raw, + self.raw, + rects.as_ptr() as *mut _, + (rects.len() * 4) as _, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + fn raw_attribute(&self, attr: EGLint) -> EGLint { + unsafe { + let mut value = 0; + self.display.inner.egl.QuerySurface(self.display.inner.raw, self.raw, attr, &mut value); + value + } + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + self.display.inner.egl.DestroySurface(self.display.inner.raw, self.raw); + } + } +} + +#[cfg(wayland_platform)] +use wayland_sys::{egl::*, ffi_dispatch}; + +enum NativeWindow { + #[cfg(wayland_platform)] + Wayland(*mut ffi::c_void), + + #[cfg(x11_platform)] + 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(wayland_platform)] + RawWindowHandle::Wayland(window_handle) => unsafe { + let ptr = ffi_dispatch!( + WAYLAND_EGL_HANDLE, + wl_egl_window_create, + window_handle.surface.cast(), + _width.get() as _, + _height.get() as _ + ); + if ptr.is_null() { + return Err(ErrorKind::OutOfMemory.into()); + } + Self::Wayland(ptr.cast()) + }, + #[cfg(x11_platform)] + 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(wayland_platform)] + Self::Wayland(wl_egl_surface) => unsafe { + ffi_dispatch!( + WAYLAND_EGL_HANDLE, + wl_egl_window_resize, + *wl_egl_surface as _, + _width.get() as _, + _height.get() as _, + 0, + 0 + ) + }, + #[cfg(x11_platform)] + Self::Xlib(_) => (), + Self::Android(_) => (), + Self::Win32(_) => (), + Self::Gbm(_) => (), + } + } + + pub(crate) fn raw(&self) -> *mut ffi::c_void { + match self { + #[cfg(wayland_platform)] + Self::Wayland(wl_egl_surface) => *wl_egl_surface, + #[cfg(x11_platform)] + 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, + } + } +} + +#[cfg(wayland_platform)] +impl Drop for NativeWindow { + fn drop(&mut self) { + unsafe { + if let Self::Wayland(wl_egl_window) = self { + ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, wl_egl_window.cast()); + } + } + } +} diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs new file mode 100644 index 0000000000..d032f67b27 --- /dev/null +++ b/glutin/src/api/glx/config.rs @@ -0,0 +1,306 @@ +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, ConfigExtraSupport, ConfigSurfaceTypes, ConfigTemplate, + GlConfig, RawConfig, +}; +use crate::display::GetGlDisplay; +use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB}; +use crate::private::Sealed; + +use super::display::Display; + +impl Display { + pub(crate) 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); + } + }; + + let mut extra = ConfigExtraSupport::empty(); + + // Render type. + config_attributes.push(glx::RENDER_TYPE as c_int); + if self.inner.client_extensions.contains("GLX_ARB_fbconfig_float") { + extra |= ConfigExtraSupport::FLOAT_PIXELS; + } + + if template.float_pixels && extra.contains(ConfigExtraSupport::FLOAT_PIXELS) { + config_attributes.push(glx_extra::RGBA_FLOAT_BIT_ARB as c_int); + } else if template.float_pixels { + return None; + } else { + config_attributes.push(glx::RGBA_BIT 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.inner.glx.ChooseFBConfig( + self.inner.raw.cast(), + self.inner.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(move |raw| { + let inner = Arc::new(ConfigInner { display: self.clone(), raw, extra }); + Config { inner } + }) + .filter(move |config| { + if template.transparency { + config.x11_visual().unwrap().supports_transparency() + } else { + true + } + }); + + Some(Box::new(iter)) + } + } +} + +#[derive(Clone)] +pub struct Config { + pub(crate) inner: Arc, +} + +pub(crate) struct ConfigInner { + display: Display, + extra: ConfigExtraSupport, + pub(crate) raw: GLXFBConfig, +} + +impl Sealed for Config {} + +impl GlConfig for Config { + 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!(), + } + } + + fn float_pixels(&self) -> bool { + if self.inner.extra.contains(ConfigExtraSupport::FLOAT_PIXELS) { + let render_type = self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum; + render_type == glx_extra::RGBA_FLOAT_BIT_ARB + } else { + false + } + } + + fn native_visual(&self) -> u32 { + self.raw_attribute(glx::VISUAL_ID as c_int) as u32 + } + + fn alpha_size(&self) -> u8 { + self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 + } + + fn srgb_capable(&self) -> bool { + if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 + } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + } else { + false + } + } + + fn depth_size(&self) -> u8 { + self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 + } + + fn stencil_size(&self) -> u8 { + self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 + } + + fn sample_buffers(&self) -> u8 { + self.raw_attribute(glx::SAMPLE_BUFFERS as c_int) as u8 + } + + 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 + } + + fn api(&self) -> Api { + Api::OPENGL + } +} +impl X11GlConfigExt for Config { + fn x11_visual(&self) -> Option { + unsafe { + let raw_visual = self + .inner + .display + .inner + .glx + .GetVisualFromFBConfig(self.inner.display.inner.raw.cast(), self.inner.raw); + if raw_visual.is_null() { + None + } else { + Some(X11VisualInfo::from_raw( + self.inner.display.inner.raw.cast(), + raw_visual as *mut _, + )) + } + } + } +} + +impl GetGlDisplay for Config { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl Config { + fn raw_attribute(&self, attr: c_int) -> c_int { + unsafe { + let mut val = 0; + self.inner.display.inner.glx.GetFBConfigAttrib( + self.inner.display.inner.raw.cast(), + self.inner.raw, + attr, + &mut val, + ); + val as c_int + } + } +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + RawConfig::Glx(self.inner.raw) + } +} diff --git a/glutin/src/api/glx/context.rs b/glutin/src/api/glx/context.rs new file mode 100644 index 0000000000..c958082bb7 --- /dev/null +++ b/glutin/src/api/glx/context.rs @@ -0,0 +1,249 @@ +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 glutin_glx_sys::glx_extra; + +use crate::context::{AsRawContext, ContextAttributes, RawContext}; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let mut attrs = Vec::::with_capacity(4); + 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 render_type = + if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE }; + + let config = config.clone(); + let context = self.inner.glx.CreateNewContext( + self.inner.raw.cast(), + config.inner.raw, + render_type as c_int, + shared_context, + // Direct context. + 1, + ); + + super::last_glx_error(self.inner.raw)?; + + let inner = ContextInner { display: self.clone(), config, context }; + + Ok(NotCurrentContext::new(inner)) + } + } +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread. + _nosendsync: PhantomData>, +} + +pub struct NotCurrentContext { + inner: ContextInner, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl Sealed for PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner, _nosync: PhantomData } + } +} + +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlConfig for NotCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl GetGlConfig for PossiblyCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + fn set_swap_interval(&self, interval: u16) {} + + fn is_current(&self) -> bool { + unsafe { self.inner.display.inner.glx.GetCurrentContext() == self.inner.context } + } + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { + self.inner.display.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ + } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type Surface = Surface; + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn make_current(self, surface: &Self::Surface) -> Result { + self.inner.make_current_draw_read(surface, surface)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } + + fn make_current_draw_read( + self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result { + self.inner.make_current_draw_read(surface_draw, surface_read)?; + Ok(PossiblyCurrentContext { inner: self.inner, _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) + } +} + +struct ContextInner { + display: Display, + config: Config, + context: GLXContext, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + unsafe { + if self.display.inner.glx.MakeContextCurrent( + self.display.inner.raw.cast(), + surface_draw.raw, + surface_read.raw, + self.context, + ) == 0 + { + super::last_glx_error(self.display.inner.raw) + } else { + Ok(()) + } + } + } + + fn make_not_current(&self) -> Result<()> { + unsafe { + if self.display.inner.glx.MakeContextCurrent( + self.display.inner.raw.cast(), + 0, + 0, + std::ptr::null(), + ) == 0 + { + super::last_glx_error(self.display.inner.raw) + } else { + Ok(()) + } + } + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + unsafe { + self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), self.context); + } + } +} diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs new file mode 100644 index 0000000000..38ff45315f --- /dev/null +++ b/glutin/src/api/glx/display.rs @@ -0,0 +1,176 @@ +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::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; +use super::{Glx, GlxDisplay, GlxExtra, XlibErrorHookRegistrator, GLX, GLX_BASE_ERROR, GLX_EXTRA}; + +#[derive(Default, Clone, Copy)] +pub(crate) struct GlxVersion { + pub(crate) major: i32, + pub(crate) minor: i32, +} + +#[derive(Clone)] +pub struct Display { + pub(crate) inner: Arc, +} + +pub(crate) struct DisplayInner { + pub(crate) glx: &'static Glx, + pub(crate) glx_extra: Option<&'static GlxExtra>, + pub(crate) raw: GlxDisplay, + pub(crate) screen: i32, + /// Client GLX extensions. + pub(crate) client_extensions: HashSet<&'static str>, +} + +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 = get_extensions(glx, display); + + let inner = Arc::new(DisplayInner { + raw: display, + glx, + glx_extra: GLX_EXTRA.as_ref(), + screen, + client_extensions, + }); + + Ok(Self { inner }) + } +} + +impl Sealed for Display {} + +impl GlDisplay for Display { + type WindowSurface = Surface; + type PixmapSurface = Surface; + type PbufferSurface = Surface; + type Config = Config; + type NotCurrentContext = NotCurrentContext; + + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Glx(self.inner.raw.cast()) + } +} + +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 5885a49d2a..0000000000 --- a/glutin/src/api/glx/make_current_guard.rs +++ /dev/null @@ -1,78 +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 = if self.old_display.is_null() { self.display } else { self.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 08ef63c9de..acf804d744 100644 --- a/glutin/src/api/glx/mod.rs +++ b/glutin/src/api/glx/mod.rs @@ -1,775 +1,174 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#![cfg(feature = "x11")] - -mod make_current_guard; - -use std::ffi::{CStr, CString}; +use std::ffi::{self, CStr, CString}; use std::ops::{Deref, DerefMut}; -use std::os::raw; -use std::sync::Arc; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Mutex; -use glutin_glx_sys as ffi; +use libloading::Library; use once_cell::sync::Lazy; -use winit::dpi; - -use self::make_current_guard::MakeCurrentGuard; -use crate::api::dlloader::{SymTrait, SymWrapper}; -use crate::platform::unix::x11::XConnection; -use crate::platform_impl::x11_utils::SurfaceType; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; - -#[derive(Clone)] -pub struct Glx(SymWrapper); - -/// Because `*const raw::c_void` doesn't implement `Sync`. -unsafe impl Sync for Glx {} - -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(Glx) - } -} - -impl Deref for Glx { - type Target = ffi::glx::Glx; +use x11_dl::xlib::{self, XErrorEvent}; - fn deref(&self) -> &ffi::glx::Glx { - &self.0 - } -} +use glutin_glx_sys::glx; +use glutin_glx_sys::glx::types::Display as GLXDisplay; +use glutin_glx_sys::glx_extra; -impl DerefMut for Glx { - fn deref_mut(&mut self) -> &mut ffi::glx::Glx { - &mut self.0 - } -} +use crate::error::{Error, ErrorKind, Result}; +use crate::lib_loading::{SymLoading, SymWrapper}; +use crate::platform::x11::XLIB; -pub static GLX: Lazy> = Lazy::new(|| 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. - #[allow(clippy::new_ret_no_self)] - 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) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - let glx = GLX.as_ref().unwrap(); - unsafe { glx.GetCurrentContext() == self.context } - } + // 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 get_api(&self) -> crate::Api { - crate::Api::OpenGl - } + *LAST_GLX_ERROR.lock().unwrap() = + Some(Error::new(Some(code as _), Some(description), kind)); - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::GLXContext { - self.context + true } +} - #[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 _ } +/// 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 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(()) - } + // 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 buffer_age(&self) -> u32 { - let glx = GLX.as_ref().unwrap(); - - let mut buffer_age = 0; +pub(crate) struct Glx(pub SymWrapper); - unsafe { - glx.QueryDrawable( - self.xconn.display as *mut _, - self.drawable, - ffi::glx_extra::BACK_BUFFER_AGE_EXT as i32, - &mut buffer_age, - ); - } +pub(crate) struct GlxExtra(glx_extra::Glx); - buffer_age - } +#[derive(Clone, Copy)] +pub(crate) struct GlxDisplay(*mut GLXDisplay); - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() +impl Deref for GlxDisplay { + type Target = *mut GLXDisplay; + fn deref(&self) -> &Self::Target { + &self.0 } } -unsafe impl Send for Context {} -unsafe impl Sync for Context {} +unsafe impl Sync for Glx {} +unsafe impl Sync for GlxExtra {} -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(ContextError::OsError) - .unwrap(); +static GLX: Lazy> = Lazy::new(|| { + let paths = ["libGL.so.1", "libGL.so"]; - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(!gl_finish_fn.is_null()); - let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn); - gl_finish_fn(); + SymWrapper::new(&paths).map(Glx).ok() +}); - if guard.old_context() == Some(self.context) { - guard.invalidate() - } - std::mem::drop(guard); +static GLX_EXTRA: Lazy> = Lazy::new(|| { + let glx = GLX.as_ref()?; + Some(GlxExtra::new(glx)) +}); - 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(CreationError::OsError)?; - - 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 |= 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 |= 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 |= 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(); +impl Deref for GlxExtra { + type Target = glx_extra::Glx; - 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); - - 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; - - (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(' ').any(|s| s == ext) + 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 0000000000..53b78e6f76 --- /dev/null +++ b/glutin/src/api/glx/surface.rs @@ -0,0 +1,223 @@ +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::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::private::Sealed; +use crate::surface::{ + AsRawSurface, GlSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, + SurfaceAttributes, SurfaceType, SurfaceTypeTrait, WindowSurface, +}; + +use super::config::Config; +use super::display::Display; + +const ATTR_SIZE_HINT: usize = 8; + +impl Display { + pub(crate) unsafe 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::::with_capacity(ATTR_SIZE_HINT); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + let config = config.clone(); + let surface = self.inner.glx.CreatePixmap( + self.inner.raw.cast(), + config.inner.raw, + xid, + attrs.as_ptr(), + ); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { display: self.clone(), config, raw: surface, _ty: PhantomData }) + } + + pub(crate) 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::::with_capacity(ATTR_SIZE_HINT); + + 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 config = config.clone(); + let surface = self.inner.glx.CreatePbuffer( + self.inner.raw.cast(), + config.inner.raw, + attrs.as_ptr(), + ); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { display: self.clone(), config, raw: surface, _ty: PhantomData }) + } + } + + pub(crate) unsafe 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::::with_capacity(ATTR_SIZE_HINT); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + let config = config.clone(); + let surface = self.inner.glx.CreateWindow( + self.inner.raw.cast(), + config.inner.raw, + window, + attrs.as_ptr() as *const _, + ); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { display: self.clone(), config, raw: surface, _ty: PhantomData }) + } +} + +pub struct Surface { + display: Display, + config: Config, + pub(crate) raw: GLXWindow, + _ty: PhantomData, +} + +impl Sealed for Surface {} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + RawSurface::Glx(self.raw as u64) + } +} + +impl GlSurface for Surface { + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) as u32 + } + + fn width(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + fn height(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + fn is_single_buffered(&self) -> bool { + // TODO + false + } + + fn swap_buffers(&self) -> Result<()> { + unsafe { + self.display.inner.glx.SwapBuffers(self.display.inner.raw.cast(), self.raw); + super::last_glx_error(self.display.inner.raw) + } + } + + fn is_current(&self) -> bool { + self.is_current_draw() && self.is_current_read() + } + + fn is_current_draw(&self) -> bool { + unsafe { self.display.inner.glx.GetCurrentDrawable() == self.raw } + } + + fn is_current_read(&self) -> bool { + unsafe { self.display.inner.glx.GetCurrentReadDrawable() == self.raw } + } + + fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { + // This isn't supported with GLXDrawable. + } +} + +impl GetGlConfig for Surface { + type Target = Config; + fn config(&self) -> Self::Target { + self.config.clone() + } +} + +impl GetGlDisplay for Surface { + type Target = Display; + fn display(&self) -> Self::Target { + self.display.clone() + } +} + +impl 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.display.inner.glx.QueryDrawable( + self.display.inner.raw.cast(), + self.raw, + attr, + &mut value, + ); + value + } + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + match T::surface_type() { + SurfaceType::Pbuffer => { + self.display.inner.glx.DestroyPbuffer(self.display.inner.raw.cast(), self.raw); + } + SurfaceType::Window => { + self.display.inner.glx.DestroyWindow(self.display.inner.raw.cast(), self.raw); + } + SurfaceType::Pixmap => { + self.display.inner.glx.DestroyPixmap(self.display.inner.raw.cast(), self.raw); + } + } + } + } +} diff --git a/glutin/src/api/ios/mod.rs b/glutin/src/api/ios/mod.rs deleted file mode 100644 index cc40fc01bf..0000000000 --- a/glutin/src/api/ios/mod.rs +++ /dev/null @@ -1,450 +0,0 @@ -#![cfg(target_os = "ios")] -#![allow(clippy::let_unit_value)] -//! 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 (ffi::kEAGLRenderingAPIOpenGLES1..=ffi::kEAGLRenderingAPIOpenGLES3).contains(&version) { - 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"; - - 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 _ - } - } - - #[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 { - Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`") - as *const objc::runtime::Class - } - - 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 8f438ccd1f..0e198f3ab5 100644 --- a/glutin/src/api/mod.rs +++ b/glutin/src/api/mod.rs @@ -1,6 +1,8 @@ -pub mod dlloader; +#[cfg(cgl_backend)] +pub mod cgl; +#[cfg(egl_backend)] pub mod egl; +#[cfg(glx_backend)] pub mod glx; -pub mod ios; -pub mod osmesa; +#[cfg(wgl_backend)] pub mod wgl; diff --git a/glutin/src/api/osmesa/mod.rs b/glutin/src/api/osmesa/mod.rs deleted file mode 100644 index 13035f98ed..0000000000 --- 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); - } - _ => (), - } - - // 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/config.rs b/glutin/src/api/wgl/config.rs new file mode 100644 index 0000000000..7ed8562ea7 --- /dev/null +++ b/glutin/src/api/wgl/config.rs @@ -0,0 +1,89 @@ +use std::os::raw::c_int; +use std::sync::Arc; + +use crate::config::{ + Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, +}; +use crate::display::GetGlDisplay; +use crate::private::Sealed; + +use super::display::Display; + +impl Display { + pub(crate) fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + todo!() + } +} + +#[derive(Clone)] +pub struct Config { + pub(crate) inner: Arc, +} + +pub(crate) struct ConfigInner {} + +impl Sealed for Config {} + +impl GlConfig for Config { + fn color_buffer_type(&self) -> ColorBufferType { + todo!() + } + + fn float_pixels(&self) -> bool { + todo!() + } + + fn native_visual(&self) -> u32 { + todo!() + } + + fn alpha_size(&self) -> u8 { + todo!() + } + + fn srgb_capable(&self) -> bool { + todo!() + } + + fn depth_size(&self) -> u8 { + todo!() + } + + fn stencil_size(&self) -> u8 { + todo!() + } + + fn sample_buffers(&self) -> u8 { + todo!() + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + todo!() + } + + fn api(&self) -> Api { + todo!() + } +} + +impl GetGlDisplay for Config { + type Target = Display; + fn display(&self) -> Self::Target { + todo!() + } +} + +impl Config { + fn raw_attribute(&self, attr: c_int) -> c_int { + todo!() + } +} + +impl AsRawConfig for Config { + fn raw_config(&self) -> RawConfig { + todo!() + } +} diff --git a/glutin/src/api/wgl/context.rs b/glutin/src/api/wgl/context.rs new file mode 100644 index 0000000000..06030e6166 --- /dev/null +++ b/glutin/src/api/wgl/context.rs @@ -0,0 +1,183 @@ +use std::cell::Cell; +use std::ffi::{self, CStr}; +use std::marker::PhantomData; +use std::rc::Rc; + +use crate::context::{AsRawContext, ContextAttributes, RawContext}; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::Result; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + todo!() + } +} + +pub struct PossiblyCurrentContext { + inner: ContextInner, + // The context could be current only on the one thread. + _nosendsync: PhantomData>, +} + +pub struct NotCurrentContext { + inner: ContextInner, + // Only non-current context could be send between threads safely. + _nosync: PhantomData>, +} + +impl Sealed for PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner, _nosync: PhantomData } + } +} + +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlConfig for NotCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl GetGlConfig for PossiblyCurrentContext { + type Target = Config; + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn update_after_resize(&self) { + self.inner.update_after_resize() + } + + fn set_swap_interval(&self, interval: u16) {} + + fn is_current(&self) -> bool { + todo!() + } + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + todo!() + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type Surface = Surface; + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn make_current(self, surface: &Self::Surface) -> Result { + self.inner.make_current_draw_read(surface, surface)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } + + fn make_current_draw_read( + self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result { + self.inner.make_current_draw_read(surface_draw, surface_read)?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + todo!() + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + todo!() + } +} + +struct ContextInner { + display: Display, + config: Config, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + todo!() + } + + fn make_not_current(&self) -> Result<()> { + todo!() + } + + fn update_after_resize(&self) { + // This line is intentionally left blank. + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + todo!() + } +} diff --git a/glutin/src/api/wgl/display.rs b/glutin/src/api/wgl/display.rs new file mode 100644 index 0000000000..8159db551e --- /dev/null +++ b/glutin/src/api/wgl/display.rs @@ -0,0 +1,92 @@ +use std::collections::HashSet; +use std::sync::Arc; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::Result; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; + +#[derive(Clone)] +pub struct Display { + pub(crate) inner: Arc, +} + +pub(crate) struct DisplayInner { + /// Client WGL extensions. + pub(crate) client_extensions: HashSet<&'static str>, +} + +impl Display { + /// Create WGL display. + /// + /// # Safety + /// + /// The `display` must point to the valid Windows display. + pub unsafe fn from_raw(display: RawDisplayHandle) -> Result { + todo!() + } +} + +impl Sealed for Display {} + +impl GlDisplay for Display { + type WindowSurface = Surface; + type PixmapSurface = Surface; + type PbufferSurface = Surface; + type Config = Config; + type NotCurrentContext = NotCurrentContext; + + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + // RawDisplay::Wgl(self.inner.raw.cast()) + todo!() + } +} 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 c343ec2c67..0000000000 --- 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 ad83595eae..26e8206d80 100644 --- a/glutin/src/api/wgl/mod.rs +++ b/glutin/src/api/wgl/mod.rs @@ -1,898 +1,4 @@ -#![cfg(any(target_os = "windows"))] - -mod make_current_guard; - -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); - } - } -} - -/// 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 _); - } - } -} - -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 { - String::new() - }; - - let use_arb_for_pixel_format = extensions.split(' ').any(|i| i == "WGL_ARB_pixel_format"); - - // 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(' ').any(|i| i == "WGL_EXT_swap_control") { - 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(' ').any(|i| i == "WGL_ARB_create_context") { - 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(' ').any(|i| i == "WGL_EXT_create_context_es2_profile") { - 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(' ').any(|i| i == "WGL_ARB_create_context_profile") { - 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(' ').any(|i| i == "WGL_ARB_create_context_robustness") { - 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 |= 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 |= 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 |= 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() && 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(' ').any(|i| i == "WGL_ARB_pixel_format_float") { - 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(' ').any(|i| i == "WGL_ARB_multisample") { - 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(' ').any(|i| i == "WGL_ARB_framebuffer_sRGB") { - 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(' ').any(|i| i == "WGL_EXT_framebuffer_sRGB") { - 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(' ').any(|i| i == "WGL_ARB_context_flush_control") { - 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(' ').any(|i| i == "WGL_ARB_multisample") { - match get_info(gl::wgl_extra::SAMPLES_ARB) { - 0 => None, - a => Some(a as u16), - } - } else { - None - } - }, - srgb: if extensions.split(' ').any(|i| i == "WGL_ARB_framebuffer_sRGB") { - get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 - } else if extensions.split(' ').any(|i| i == "WGL_EXT_framebuffer_sRGB") { - 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, - }; - - // now querying - let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) }; - if pf_id == 0 { - return Err(CreationError::OsError("No available pixel format".to_owned())); - } - - Ok(pf_id) -} +pub mod config; +pub mod context; +pub mod display; +pub mod surface; diff --git a/glutin/src/api/wgl/surface.rs b/glutin/src/api/wgl/surface.rs new file mode 100644 index 0000000000..a5164c0145 --- /dev/null +++ b/glutin/src/api/wgl/surface.rs @@ -0,0 +1,123 @@ +use std::marker::PhantomData; +use std::num::NonZeroU32; +use std::os::raw::{c_int, c_uint}; + +use raw_window_handle::RawWindowHandle; + +use crate::config::GetGlConfig; +use crate::display::GetGlDisplay; +use crate::error::Result; +use crate::private::Sealed; +use crate::surface::{ + AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, + SurfaceType, SurfaceTypeTrait, WindowSurface, +}; + +use super::config::Config; +use super::display::Display; + +impl Display { + pub(crate) unsafe fn create_pixmap_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } + + pub(crate) fn create_pbuffer_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } + + pub(crate) unsafe fn create_window_surface( + &self, + config: &Config, + surface_attributes: &SurfaceAttributes, + ) -> Result> { + todo!() + } +} + +pub struct Surface { + display: Display, + config: Config, + _ty: PhantomData, +} + +impl Sealed for Surface {} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + todo!() + } +} + +impl GlSurface for Surface { + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + todo!() + } + + fn width(&self) -> Option { + todo!() + } + + fn height(&self) -> Option { + todo!() + } + + fn is_single_buffered(&self) -> bool { + todo!() + } + + fn swap_buffers(&self) -> Result<()> { + todo!() + } + + fn is_current(&self) -> bool { + todo!() + } + + fn is_current_draw(&self) -> bool { + todo!() + } + + fn is_current_read(&self) -> bool { + todo!() + } + + fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { + // This isn't supported with GLXDrawable. + } +} + +impl GetGlConfig for Surface { + type Target = Config; + fn config(&self) -> Self::Target { + self.config.clone() + } +} + +impl GetGlDisplay for Surface { + type Target = Display; + fn display(&self) -> Self::Target { + self.display.clone() + } +} + +impl Surface { + fn raw_attribute(&self, attr: c_int) -> c_uint { + todo!() + } +} + +impl Drop for Surface { + fn drop(&mut self) { + todo!() + } +} diff --git a/glutin/src/config.rs b/glutin/src/config.rs new file mode 100644 index 0000000000..0be0011e59 --- /dev/null +++ b/glutin/src/config.rs @@ -0,0 +1,438 @@ +//! GL config picking and creating utils. + +use std::ffi; +use std::num::NonZeroU32; + +use bitflags::bitflags; + +use crate::dispatch_gl; +use crate::display::{Display, GetGlDisplay}; +use crate::private::Sealed; + +#[cfg(x11_platform)] +use crate::platform::x11::{X11GlConfigExt, X11VisualInfo}; + +#[cfg(cgl_backend)] +use crate::api::cgl::config::Config as CglConfig; +#[cfg(egl_backend)] +use crate::api::egl::config::Config as EglConfig; +#[cfg(glx_backend)] +use crate::api::glx::config::Config as GlxConfig; +#[cfg(wgl_backend)] +use crate::api::wgl::config::Config as WglConfig; + +bitflags! { + /// Extra config bits that require separate extensions. + pub(crate) struct ConfigExtraSupport: u8 { + const FLOAT_PIXELS = 0b0000_0001; + const SRGB = 0b0000_0010; + } +} + +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. +#[derive(Clone)] +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 config uses floating pixels. + pub(crate) float_pixels: bool, + + /// 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 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, + + float_pixels: 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 + } + + /// Wether the floating pixel formats should be used. + /// + /// By default `false` is requested. + #[inline] + pub fn with_float_pixels(mut self, float_pixels: bool) -> Self { + self.template.float_pixels = float_pixels; + 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 + } +} + +pub trait GlConfig: Sealed { + /// The type of the underlying color buffer. + fn color_buffer_type(&self) -> ColorBufferType; + + /// Whether the config uses floating pixels. + fn float_pixels(&self) -> bool; + + /// The native visual identifier. + fn native_visual(&self) -> u32; + + /// The size of the alpha. + fn alpha_size(&self) -> u8; + + /// The size of the depth buffer. + fn depth_size(&self) -> u8; + + /// The size of the stencil buffer. + fn stencil_size(&self) -> u8; + + /// The number of sample buffers used for multisampling. + fn sample_buffers(&self) -> u8; + + /// Whether the config supports creating srgb capable [`Surface`]. + fn srgb_capable(&self) -> bool; + + /// The type of the surfaces that can be created with this config. + fn config_surface_types(&self) -> ConfigSurfaceTypes; + + /// The [`Api`] supported by the configuration. + fn api(&self) -> Api; +} + +pub trait GetGlConfig: Sealed { + type Target: GlConfig; + fn config(&self) -> Self::Target; +} + +/// The GL configuration used to create [`Surface`] and [`Context`] in a cross platform way. +#[derive(Clone)] +pub enum Config { + #[cfg(egl_backend)] + Egl(EglConfig), + + #[cfg(glx_backend)] + Glx(GlxConfig), + + #[cfg(wgl_backend)] + Wgl(WglConfig), + + #[cfg(cgl_backend)] + Cgl(CglConfig), +} + +impl GlConfig for Config { + fn color_buffer_type(&self) -> ColorBufferType { + dispatch_gl!(self; Self(config) => config.color_buffer_type()) + } + + fn float_pixels(&self) -> bool { + dispatch_gl!(self; Self(config) => config.float_pixels()) + } + + fn native_visual(&self) -> u32 { + dispatch_gl!(self; Self(config) => config.native_visual()) + } + + fn alpha_size(&self) -> u8 { + dispatch_gl!(self; Self(config) => config.alpha_size()) + } + + fn depth_size(&self) -> u8 { + dispatch_gl!(self; Self(config) => config.depth_size()) + } + + fn stencil_size(&self) -> u8 { + dispatch_gl!(self; Self(config) => config.stencil_size()) + } + + fn sample_buffers(&self) -> u8 { + dispatch_gl!(self; Self(config) => config.sample_buffers()) + } + + fn srgb_capable(&self) -> bool { + dispatch_gl!(self; Self(config) => config.srgb_capable()) + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + dispatch_gl!(self; Self(config) => config.config_surface_types()) + } + + fn api(&self) -> Api { + dispatch_gl!(self; Self(config) => config.api()) + } +} + +#[cfg(x11_platform)] +impl X11GlConfigExt for Config { + fn x11_visual(&self) -> Option { + dispatch_gl!(self; Self(config) => config.x11_visual()) + } +} + +impl Sealed for Config {} + +impl GetGlDisplay for Config { + type Target = Display; + fn display(&self) -> Self::Target { + dispatch_gl!(self; Self(config) => config.display(); as Display) + } +} + +/// Raw config. +pub enum RawConfig { + /// Raw EGL config. + #[cfg(egl_backend)] + Egl(*const ffi::c_void), + + /// Raw GLX config. + #[cfg(glx_backend)] + Glx(*const ffi::c_void), + + /// TODO + #[cfg(wgl_backend)] + Wgl(*const ffi::c_void), + + /// TODO + #[cfg(cgl_backend)] + Cgl(*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 { + dispatch_gl!(self; Self(config) => config.raw_config()) + } +} diff --git a/glutin/src/context.rs b/glutin/src/context.rs index 715df05e22..6277f31632 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -1,168 +1,410 @@ -use super::*; +use std::ffi::{self, CStr}; -use std::marker::PhantomData; -use winit::event_loop::EventLoopWindowTarget; +use crate::dispatch_gl; +use crate::display::{Display, GetGlDisplay}; +use crate::error::Result; +use crate::private::Sealed; +use crate::surface::{GlSurface, Surface, SurfaceTypeTrait}; -/// 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`]. +#[cfg(cgl_backend)] +use crate::api::cgl::context::{ + NotCurrentContext as NotCurrentCglContext, PossiblyCurrentContext as PossiblyCurrentCglContext, +}; +#[cfg(egl_backend)] +use crate::api::egl::context::{ + NotCurrentContext as NotCurrentEglContext, PossiblyCurrentContext as PossiblyCurrentEglContext, +}; +#[cfg(glx_backend)] +use crate::api::glx::context::{ + NotCurrentContext as NotCurrentGlxContext, PossiblyCurrentContext as PossiblyCurrentGlxContext, +}; +#[cfg(wgl_backend)] +use crate::api::wgl::context::{ + NotCurrentContext as NotCurrentWglContext, PossiblyCurrentContext as PossiblyCurrentWglContext, +}; + +/// 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. /// -/// # Example +/// [`Context`]: crate::context::Context +#[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`]: crate::context::Robustness::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, + + /// Everything is checked to avoid any crash. If a problem occurs, the + /// context will enter a "context lost" state. It must then be + /// recreated. + RobustLoseContextOnReset, +} + +impl Default for Robustness { + #[inline] + fn default() -> Self { + Robustness::NotRobust + } +} + +/// Describes the requested OpenGL [`Context`] profiles. /// -/// ```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()); -/// # } -/// ``` -#[derive(Debug)] -pub struct Context { - pub(crate) context: platform_impl::Context, - pub(crate) phantom: PhantomData, -} - -impl Context { - /// See [`ContextWrapper::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)), - } +/// [`Context`]: crate::context::Context +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GlProfile { + /// Include all the immediate more functions and definitions. + /// + /// # Api specific + /// + /// Given that EGL does't support profiles it'll be used to represent OpenGL ES Api. + Compatibility, + /// Include all the future-compatible functions and definitions. + /// + /// # Api specific + /// + /// Given that EGL does't support profiles it'll be used to represent OpenGL Api. + Core, +} + +/// The behavior of the driver when you change the current context. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ReleaseBehaviour { + /// Doesn't do anything. Most notably doesn't flush. Not supported by all + /// drivers. + None, + + /// Flushes the context that was previously current as if `glFlush` was + /// called. This is the default behaviour. + Flush, +} + +impl Default for ReleaseBehaviour { + #[inline] + fn default() -> Self { + ReleaseBehaviour::Flush } +} - /// See [`ContextWrapper::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)), - } +/// The attributes that are used to create a graphics context. +#[derive(Default)] +pub struct ContextAttributes { + pub(crate) release_behavior: ReleaseBehaviour, + + 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: ReleaseBehaviour) -> Self { + self.attributes.release_behavior = release_behavior; + self + } + + pub fn build(self) -> ContextAttributes { + self.attributes + } +} + +pub trait PossiblyCurrentGlContext: Sealed { + type NotCurrentContext: NotCurrentGlContext; + + fn is_current(&self) -> bool; + + fn make_not_current(self) -> Result; + + fn set_swap_interval(&self, interval: u16); + + fn update_after_resize(&self); + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void; +} + +pub trait PossiblyCurrentContextGlSurfaceAccessor: Sealed { + type Surface: GlSurface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()>; + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()>; +} + +pub trait NotCurrentGlContext: Sealed { + type PossiblyCurrentContext: PossiblyCurrentGlContext; + + fn treat_as_current(self) -> Self::PossiblyCurrentContext; +} + +pub trait NotCurrentGlContextSurfaceAccessor: Sealed { + type Surface: GlSurface; + type PossiblyCurrentContext: PossiblyCurrentGlContext; + + fn make_current(self, surface: &Self::Surface) -> Result; + + fn make_current_draw_read( + self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result; +} + +pub enum PossiblyCurrentContext { + #[cfg(egl_backend)] + Egl(PossiblyCurrentEglContext), + + #[cfg(glx_backend)] + Glx(PossiblyCurrentGlxContext), + + #[cfg(wgl_backend)] + Wgl(PossiblyCurrentWglContext), + + #[cfg(cgl_backend)] + Cgl(PossiblyCurrentCglContext), +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn is_current(&self) -> bool { + dispatch_gl!(self; Self(context) => context.is_current()) } - /// See [`ContextWrapper::treat_as_not_current()`]. - pub unsafe fn treat_as_not_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } + fn make_not_current(self) -> Result { + Ok(dispatch_gl!(self; Self(context) => context.make_not_current()?; as NotCurrentContext)) } - /// See [`ContextWrapper::treat_as_current()`]. - pub unsafe fn treat_as_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } + fn set_swap_interval(&self, interval: u16) { + dispatch_gl!(self; Self(context) => context.set_swap_interval(interval)) } - /// See [`ContextWrapper::is_current()`]. - pub fn is_current(&self) -> bool { - self.context.is_current() + fn update_after_resize(&self) { + dispatch_gl!(self; Self(context) => context.update_after_resize()) } - /// See [`ContextWrapper::get_api()`]. - pub fn get_api(&self) -> Api { - self.context.get_api() + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + dispatch_gl!(self; Self(context) => context.get_proc_address(addr)) } } -impl Context { - /// See [`ContextWrapper::get_proc_address()`]. - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.context.get_proc_address(addr) +impl Sealed for PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + match (self, surface) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface), + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface), + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(surface)) => context.make_current(surface), + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(surface)) => context.make_current(surface), + _ => unreachable!(), + } + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + match (self, surface_draw, surface_read) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { + context.make_current_draw_read(draw, read) + } + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { + context.make_current_draw_read(draw, read) + } + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { + context.make_current_draw_read(draw, read) + } + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { + context.make_current_draw_read(draw, read) + } + _ => unreachable!(), + } } } -#[allow(rustdoc::broken_intra_doc_links)] -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()`][Self::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. - #[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\n - [`build_surfaceless()`]: crate::platform\n - [`build_osmesa()`]: crate::platform - " - )] - #[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\n - [`build_surfaceless()`]: platform::unix::HeadlessContextExt::build_surfaceless()\n - [`build_osmesa()`]: platform::unix::HeadlessContextExt::build_osmesa() - " - )] - pub fn build_headless( +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + dispatch_gl!(self; Self(context) => context.display(); as Display) + } +} + +pub enum NotCurrentContext { + #[cfg(egl_backend)] + Egl(NotCurrentEglContext), + + #[cfg(glx_backend)] + Glx(NotCurrentGlxContext), + + #[cfg(wgl_backend)] + Wgl(NotCurrentWglContext), + + #[cfg(cgl_backend)] + Cgl(NotCurrentCglContext), +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; + + fn make_current(self, surface: &Self::Surface) -> Result { + match (self, surface) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(surface)) => { + Ok(PossiblyCurrentContext::Egl(context.make_current(surface)?)) + } + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(surface)) => { + Ok(PossiblyCurrentContext::Glx(context.make_current(surface)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(surface)) => { + Ok(PossiblyCurrentContext::Wgl(context.make_current(surface)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(surface)) => { + Ok(PossiblyCurrentContext::Cgl(context.make_current(surface)?)) + } + _ => unreachable!(), + } + } + + 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 }) + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result { + match (self, surface_draw, surface_read) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { + Ok(PossiblyCurrentContext::Egl(context.make_current_draw_read(draw, read)?)) + } + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { + Ok(PossiblyCurrentContext::Glx(context.make_current_draw_read(draw, read)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { + Ok(PossiblyCurrentContext::Wgl(context.make_current_draw_read(draw, read)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { + Ok(PossiblyCurrentContext::Cgl(context.make_current_draw_read(draw, read)?)) + } + _ => unreachable!(), + } } } -// This is nightly only: -// impl !Send for Context {} -// impl !Sync for Context {} -// -// Instead we add a phantom type to PossiblyCurrent +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; -/// 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. -#[derive(Debug, Clone, Copy)] -pub struct PossiblyCurrent { - phantom: PhantomData<*mut ()>, + fn treat_as_current(self) -> Self::PossiblyCurrentContext { + dispatch_gl!(self; Self(context) => context.treat_as_current(); as PossiblyCurrentContext) + } } -/// A type that [`Context`]s which are not currently current on any thread take -/// as a generic. -/// -/// See [`ContextWrapper::make_current()`] for more details. -#[derive(Debug, Clone, Copy)] -pub enum NotCurrent {} +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + fn display(&self) -> Self::Target { + dispatch_gl!(self; Self(context) => context.display(); as Display) + } +} -/// A trait implemented on both [`NotCurrent`] and -/// [`PossiblyCurrent`]. -pub trait ContextCurrentState: std::fmt::Debug + Clone {} +/// Raw context. +pub enum RawContext { + /// Raw EGL context. + #[cfg(egl_backend)] + Egl(*const ffi::c_void), -impl ContextCurrentState for PossiblyCurrent {} -impl ContextCurrentState for NotCurrent {} + /// Raw GLX context. + #[cfg(glx_backend)] + Glx(*const ffi::c_void), + + /// TODO. + #[cfg(wgl_backend)] + Wgl(*const ffi::c_void), + + /// TODO. + #[cfg(cgl_backend)] + Cgl(*const ffi::c_void), +} -trait FailToCompileIfNotSendSync -where - Self: Send + Sync, -{ +pub trait AsRawContext { + fn raw_context(&self) -> RawContext; +} + +impl AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + dispatch_gl!(self; Self(context) => context.raw_context()) + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + dispatch_gl!(self; Self(context) => context.raw_context()) + } } -impl FailToCompileIfNotSendSync for Context {} diff --git a/glutin/src/display.rs b/glutin/src/display.rs new file mode 100644 index 0000000000..787c3c3ad1 --- /dev/null +++ b/glutin/src/display.rs @@ -0,0 +1,419 @@ +//! The GL platform display creation and picking. + +use std::ffi; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::{Config, ConfigTemplate, GlConfig}; +use crate::context::{ContextAttributes, NotCurrentContext, NotCurrentGlContext}; +use crate::dispatch_gl; +use crate::error::Result; +use crate::private::Sealed; +use crate::surface::{ + GlSurface, PbufferSurface, PixmapSurface, Surface, SurfaceAttributes, WindowSurface, +}; + +#[cfg(cgl_backend)] +use crate::api::cgl::display::Display as CglDisplay; +#[cfg(egl_backend)] +use crate::api::egl::display::Display as EglDisplay; +#[cfg(glx_backend)] +use crate::api::glx::display::Display as GlxDisplay; +#[cfg(glx_backend)] +use crate::api::glx::XlibErrorHookRegistrator; +#[cfg(wgl_backend)] +use crate::api::wgl::display::Display as WglDisplay; + +/// Preference of the display that should be used. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum DisplayApiPreference { + /// Prefer EGL. + #[cfg(egl_backend)] + Egl, + /// Prefer GLX. + #[cfg(glx_backend)] + Glx, + /// Prefer WGL. + #[cfg(wgl_backend)] + Wgl, + /// Prefer CGL. + #[cfg(cgl_backend)] + Cgl, + + /// Prefer EGL and fallback to GLX. + #[cfg(all(egl_backend, glx_backend))] + EglThenGlx, + /// Prefer GLX and fallback to EGL. + #[cfg(all(egl_backend, glx_backend))] + GlxThenEgl, + + /// Prefer EGL and fallback to GLX. + #[cfg(all(egl_backend, wgl_backend))] + EglThenWgl, + /// Prefer GLX and fallback to EGL. + #[cfg(all(egl_backend, wgl_backend))] + WglThenEgl, +} + +/// A settings to control automatic display picking. +pub struct DisplayPicker { + pub(crate) api_preference: DisplayApiPreference, + #[cfg(glx_backend)] + pub(crate) glx_error_registrator: Option, +} + +impl Default for DisplayPicker { + #[cfg(all(egl_backend, glx_backend))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl, glx_error_registrator: None } + } + + #[cfg(all(egl_backend, not(glx_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl } + } + + #[cfg(all(glx_backend, not(egl_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Glx, glx_error_registrator: None } + } + + #[cfg(all(wgl_backend, not(egl_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Wgl } + } + + #[cfg(cgl_backend)] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Cgl } + } +} + +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(glx_backend)] + pub fn with_glx_error_registrator( + mut self, + error_registrator: XlibErrorHookRegistrator, + ) -> Self { + self.glx_error_registrator = Some(error_registrator); + self + } +} + +pub trait GlDisplay: Sealed { + type WindowSurface: GlSurface; + type PixmapSurface: GlSurface; + type PbufferSurface: GlSurface; + type Config: GlConfig; + type NotCurrentContext: NotCurrentGlContext; + + /// Find configuration matching the given `template`. + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>>; + + /// Create the graphics platform context. + fn create_context( + &self, + config: &Self::Config, + context_attributes: &ContextAttributes, + ) -> Result; + + /// Create the surface that can be used to render into native window. + /// + /// # Safety + /// + /// The [`RawWindowHandle`] must point to a valid object. + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result; + + /// Create the surfate that can be used to render into pbuffer. + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result; + + /// Create the surface that can be used to render into pixmap. + /// + /// # Safety + /// + /// The [`NativePixmap`] must represent a valid native pixmap. + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result; +} + +/// The graphics display to handle underlying graphics platform in a crossplatform way. +#[derive(Clone)] +pub enum Display { + /// The EGL display. + #[cfg(egl_backend)] + Egl(EglDisplay), + + /// The GLX display. + #[cfg(glx_backend)] + Glx(GlxDisplay), + + /// The WGL display. + #[cfg(wgl_backend)] + Wgl(WglDisplay), + + /// The CGL display. + #[cfg(cgl_backend)] + Cgl(CglDisplay), +} + +impl GlDisplay for Display { + type WindowSurface = Surface; + type PixmapSurface = Surface; + type PbufferSurface = Surface; + type Config = Config; + type NotCurrentContext = NotCurrentContext; + + fn find_configs( + &self, + template: ConfigTemplate, + ) -> Option + '_>> { + match self { + #[cfg(egl_backend)] + Self::Egl(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Egl))) + } + #[cfg(glx_backend)] + Self::Glx(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Glx))) + } + #[cfg(wgl_backend)] + Self::Wgl(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Wgl))) + } + #[cfg(cgl_backend)] + Self::Cgl(display) => { + Some(Box::new(display.find_configs(template)?.into_iter().map(Config::Cgl))) + } + } + } + + fn create_context( + &self, + config: &Self::Config, + context_attributes: &ContextAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(NotCurrentContext::Egl(display.create_context(config, context_attributes)?)) + } + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(NotCurrentContext::Glx(display.create_context(config, context_attributes)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(NotCurrentContext::Wgl(display.create_context(config, context_attributes)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(NotCurrentContext::Cgl(display.create_context(config, context_attributes)?)) + } + _ => unreachable!(), + } + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?)) + } + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_window_surface(config, surface_attributes)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_window_surface(config, surface_attributes)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_window_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } + + fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)?)) + } + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pbuffer_surface(config, surface_attributes)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_pbuffer_surface(config, surface_attributes)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_pbuffer_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?)) + } + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pixmap_surface(config, surface_attributes)?)) + } + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_pixmap_surface(config, surface_attributes)?)) + } + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_pixmap_surface(config, surface_attributes)?)) + } + _ => unreachable!(), + } + } +} + +impl Sealed for Display {} + +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 { + #[cfg(glx_backend)] + let registrator = picker + .glx_error_registrator + .expect("glx was requested, but error hook registrator wasn't provided."); + + match picker.api_preference { + #[cfg(egl_backend)] + DisplayApiPreference::Egl => Ok(Self::Egl(EglDisplay::from_raw(display)?)), + #[cfg(glx_backend)] + DisplayApiPreference::Glx => Ok(Self::Glx(GlxDisplay::from_raw(display, registrator)?)), + #[cfg(wgl_backend)] + DisplayApiPreference::Wgl => Ok(Self::Wgl(WglDisplay::from_raw(display)?)), + #[cfg(cgl_backend)] + DisplayApiPreference::Cgl => Ok(Self::Cgl(CglDisplay::from_raw(display)?)), + + #[cfg(all(egl_backend, glx_backend))] + 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(egl_backend, glx_backend))] + DisplayApiPreference::GlxThenEgl => { + if let Ok(display) = GlxDisplay::from_raw(display, registrator) { + Ok(Self::Glx(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } + } + + #[cfg(all(egl_backend, wgl_backend))] + DisplayApiPreference::EglThenWgl => { + if let Ok(display) = EglDisplay::from_raw(display) { + Ok(Self::Egl(display)) + } else { + Ok(Self::Wgl(WglDisplay::from_raw(display)?)) + } + } + #[cfg(all(egl_backend, wgl_backend))] + DisplayApiPreference::WglThenEgl => { + if let Ok(display) = WglDisplay::from_raw(display) { + Ok(Self::Wgl(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } + } + } + } +} + +pub trait GetGlDisplay: Sealed { + type Target: GlDisplay; + + /// Obtain the GL display used to create a particular GL object. + fn display(&self) -> Self::Target; +} + +/// Raw GL platform display. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RawDisplay { + /// Raw EGL display. + #[cfg(egl_backend)] + Egl(*const ffi::c_void), + + /// Raw GLX display. + #[cfg(glx_backend)] + Glx(*const ffi::c_void), + + /// TODO. + #[cfg(wgl_backend)] + Wgl(*const ffi::c_void), + + /// TODO + #[cfg(cgl_backend)] + Cgl(*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 { + dispatch_gl!(self; Self(display) => display.raw_display()) + } +} diff --git a/glutin/src/error.rs b/glutin/src/error.rs new file mode 100644 index 0000000000..48da770d60 --- /dev/null +++ b/glutin/src/error.rs @@ -0,0 +1,164 @@ +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: Option, + raw_os_message: Option, + kind: ErrorKind, + ) -> Self { + Self { 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 74d46b1f6e..1c1abad31a 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -1,664 +1,61 @@ -//! 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`]: crate::window::Window -#![cfg_attr( - target_os = "windows", - doc = "\ -[`RawContextExt`]: crate::platform::windows::RawContextExt -" -)] -#![cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "windows", - target_os = "openbsd", - )), - doc = "\ -[`RawContextExt`]: crate::platform -" -)] -#![cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`RawContextExt`]: crate::platform::unix::RawContextExt -" -)] -#![deny(missing_debug_implementations)] -#![allow(clippy::missing_safety_doc, clippy::too_many_arguments)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] - -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[macro_use] -extern crate objc; +//! A cross platform OpenGL platform api library. +pub mod api; +pub mod config; +pub mod context; +pub mod display; +pub mod error; pub mod platform; - -mod api; -mod context; -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. -#[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 Default for ContextBuilder<'_, NotCurrent> { - fn default() -> Self { - Self { gl_attr: Default::default(), pf_reqs: Default::default() } - } -} - -impl<'a> ContextBuilder<'a, NotCurrent> { - /// Initializes a new `ContextBuilder` with default values. - pub fn new() -> Self { - 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. - #[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`. - #[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`]. - #[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`]. - #[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) +pub mod prelude; +pub mod surface; + +mod lib_loading; + +#[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] +compile_error!("Please select at least one api backend"); + +pub(crate) mod gl_dipsatch { + /// `dispatch_gl!(match expr; Enum(foo) => foo.something())` + /// expands to the equivalent of + /// ```ignore + /// match self { + /// Enum::Egl(foo) => foo.something(), + /// Enum::Glx(foo) => foo.something(), + /// Enum::Wgl(foo) => foo.something(), + /// Enum::Cgl(foo) => foo.something(), + /// } + /// ``` + /// The result can be converted to another enum by adding `; as AnotherEnum` + #[macro_export] + macro_rules! dispatch_gl { + ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => { + match $what { + #[cfg(egl_backend)] + $enum::Egl($($c1)*) => $enum2::Egl($x), + #[cfg(glx_backend)] + $enum::Glx($($c1)*) => $enum2::Glx($x), + #[cfg(wgl_backend)] + $enum::Wgl($($c1)*) => $enum2::Wgl($x), + #[cfg(cgl_backend)] + $enum::Cgl($($c1)*) => $enum2::Cgl($x), } }; - 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(()); + ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr) => { + match $what { + #[cfg(egl_backend)] + $enum::Egl($($c1)*) => $x, + #[cfg(glx_backend)] + $enum::Glx($($c1)*) => $x, + #[cfg(wgl_backend)] + $enum::Wgl($($c1)*) => $x, + #[cfg(cgl_backend)] + $enum::Cgl($($c1)*) => $x, } - }) - } -} - -impl std::error::Error for CreationError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - CreationError::NoBackendAvailable(err) => Some(&**err), - CreationError::Window(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`]. -#[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. -#[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`. - 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. -#[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`][Self::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`][Self::RobustNoResetNotification] - /// but the context creation doesn't fail if it's not supported. - 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`][Self::RobustLoseContextOnReset] - /// but the context creation doesn't fail if it's not supported. - 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 ensure a certain visual xid is used when - /// choosing the fbconfig. - #[allow(dead_code)] - 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`]. -#[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 [`GlRequest::Latest`]. - 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 [`Robustness::TryRobustLoseContextOnReset`]. - pub robustness: Robustness, - - /// Whether to use vsync. If vsync is enabled, calling - /// [`ContextWrapper::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, - } + }; } } -// 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, +mod private { + /// Prevent traits from being implemented downstream. + pub trait Sealed {} } diff --git a/glutin/src/lib_loading.rs b/glutin/src/lib_loading.rs new file mode 100644 index 0000000000..fadf8315cc --- /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 9da067af39..0000000000 --- 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 d9073015b8..0000000000 --- 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 9e21e9211e..0000000000 --- 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 index 4ceb89a671..47cc0d7292 100644 --- a/glutin/src/platform/mod.rs +++ b/glutin/src/platform/mod.rs @@ -1,53 +1,4 @@ -//! Contains traits with platform-specific methods in them. -//! -//! Contains the following modules: -//! -//! - `android` -//! - `ios` -//! - `macos` -//! - `unix` -//! - `windows` +//! Platform specific utils to simplify interactions with the Api. -/// 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`][crate::Context]s. -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>; -} +#[cfg(x11_platform)] +pub mod x11; diff --git a/glutin/src/platform/unix.rs b/glutin/src/platform/unix.rs deleted file mode 100644 index 66aed2c4ad..0000000000 --- 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 583ef0d632..0000000000 --- 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/x11.rs b/glutin/src/platform/x11.rs new file mode 100644 index 0000000000..8cfdecfd28 --- /dev/null +++ b/glutin/src/platform/x11.rs @@ -0,0 +1,81 @@ +//! Dedicated utils to work with X11 shenanigans. + +use std::mem; + +use once_cell::sync::Lazy; +use x11_dl::xlib::{Display, XVisualInfo, Xlib}; +#[cfg(egl_backend)] +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()); + +pub trait X11GlConfigExt { + // The `X11VisualInfo` that must be used to inititalize the Xlib window. + fn x11_visual(&self) -> Option; +} + +/// The X11 visual info. +/// +/// This must be used when building X11 window, so it'll be compatible with the underlying Api. +pub struct X11VisualInfo { + display: *mut Display, + raw: *const XVisualInfo, +} + +impl X11VisualInfo { + #[cfg(egl_backend)] + 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/android/mod.rs b/glutin/src/platform_impl/android/mod.rs deleted file mode 100644 index 5c6fb0a92c..0000000000 --- a/glutin/src/platform_impl/android/mod.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![cfg(target_os = "android")] - -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::{AndroidNdkWindowHandle, 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(AndroidNdkWindowHandle { 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); - - 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() - } -} diff --git a/glutin/src/platform_impl/ios/mod.rs b/glutin/src/platform_impl/ios/mod.rs deleted file mode 100644 index fddec561b3..0000000000 --- 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/macos/helpers.rs b/glutin/src/platform_impl/macos/helpers.rs deleted file mode 100644 index 26c01c48a6..0000000000 --- a/glutin/src/platform_impl/macos/helpers.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::cmp::Ordering; - -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 - match v.cmp(&(3, 2)) { - Ordering::Less => { - if opengl.profile.is_none() && v <= (2, 1) { - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } - Ordering::Equal => Ok(NSOpenGLProfileVersion3_2Core), - Ordering::Greater => 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 6b67085c64..0000000000 --- a/glutin/src/platform_impl/macos/mod.rs +++ /dev/null @@ -1,358 +0,0 @@ -#![cfg(target_os = "macos")] -#![allow(clippy::let_unit_value)] -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::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.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 opacity = 0; - CGLSetParameter( - gl_context.CGLContextObj() as *mut _, - kCGLCPSurfaceOpacity, - &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(&self) -> &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 7d6c795393..0000000000 --- a/glutin/src/platform_impl/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub use self::platform::*; - -#[cfg(target_os = "windows")] -#[path = "windows/mod.rs"] -mod platform; -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#[path = "unix/mod.rs"] -mod platform; -#[cfg(target_os = "macos")] -#[path = "macos/mod.rs"] -mod platform; -#[cfg(target_os = "android")] -#[path = "android/mod.rs"] -mod platform; -#[cfg(target_os = "ios")] -#[path = "ios/mod.rs"] -mod platform; diff --git a/glutin/src/platform_impl/unix/mod.rs b/glutin/src/platform_impl/unix/mod.rs deleted file mode 100644 index ef72e8a68b..0000000000 --- a/glutin/src/platform_impl/unix/mod.rs +++ /dev/null @@ -1,461 +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"; - 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"; - 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"; - 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(Context::Wayland); - } - #[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(Context::X11); - } - 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`][crate::ContextBuilder] -/// which allows building unix-specific headless contexts. -pub trait HeadlessContextExt { - /// Builds an OsMesa context. - /// - /// Errors can occur if the OpenGL [`Context`][crate::Context] could not be created. - /// This generally happens because the underlying platform doesn't support a - /// requested feature. - fn build_osmesa( - self, - size: dpi::PhysicalSize, - ) -> Result, CreationError> - where - Self: Sized; - - /// Builds an EGL-surfaceless context. - /// - /// Errors can occur if the OpenGL [`Context`][crate::Context] could not be created. - /// This generally happens because the underlying platform doesn't support a - /// requested feature. - 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::OsMesa) - .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`][crate::ContextBuilder] -/// which allows assembling [`RawContext`][crate::RawContext]s. -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::Wayland) - .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::X11) - .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 bfebe469c6..0000000000 --- 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::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() - } -} diff --git a/glutin/src/platform_impl/unix/x11.rs b/glutin/src/platform_impl/unix/x11.rs deleted file mode 100644 index e85757f3a0..0000000000 --- a/glutin/src/platform_impl/unix/x11.rs +++ /dev/null @@ -1,670 +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::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, 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 preferred 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 EGL.is_some() { - return egl(builder_egl_u); - } else if GLX.is_some() { - return glx(builder_glx_u); - } - } else if GLX.is_some() { - return glx(builder_glx_u); - } else if EGL.is_some() { - return egl(builder_egl_u); - } - - return Err(CreationError::NotSupported( - "both libGL and libEGL are not present".to_string(), - )); - } else { - if prefer_egl { - if EGL.is_some() { - return egl(builder_egl_u); - } - } else if GLX.is_some() { - 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 EGL.is_some() { - 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.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(), - 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 6fd7e2991c..0000000000 --- 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); - } - } - } - - 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 deleted file mode 100644 index 72a2e568cc..0000000000 --- a/glutin/src/platform_impl/windows/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -#![cfg(target_os = "windows")] - -use crate::{ - Api, ContextCurrentState, ContextError, CreationError, GlAttributes, GlRequest, NotCurrent, - PixelFormat, PixelFormatRequirements, Rect, -}; - -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::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), -} - -#[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), -} - -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(Context::Egl) - } - // 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) } - } - } - } - - #[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(Context::EglPbuffer); - - if let Ok(context) = context { - return Ok(context); - } - } - _ => (), - } - - 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), - _ => unreachable!(), - }) - } - - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // Method is for API consistency. - } - - #[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(), - } - } - - #[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(), - } - } - - #[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(), - } - } - - #[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), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match *self { - Context::Egl(ref c) => c.buffer_age(), - _ => 0, - } - } - - #[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!(), - } - } - - #[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) -> 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(), - } - } - - #[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(), - _ => unreachable!(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { - RawHandle::Wgl(c.get_hglrc()) - } - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()), - } - } - - #[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 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: isize, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> RawContextExt for crate::ContextBuilder<'a, T> { - #[inline] - unsafe fn build_raw_context( - self, - hwnd: isize, - ) -> 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: () }) - } -} diff --git a/glutin/src/prelude.rs b/glutin/src/prelude.rs new file mode 100644 index 0000000000..0cae68e41a --- /dev/null +++ b/glutin/src/prelude.rs @@ -0,0 +1,17 @@ +//! The glutin prelude. +//! +//! The purpose of this module is to bring common imports, given that all graphics api are on the +//! traits. +//! +//! ``` +//! # #![allow(unused_imports)] +//! use glutin::prelude::*; +//! ``` + +pub use crate::config::GlConfig; +pub use crate::context::{ + NotCurrentGlContext, NotCurrentGlContextSurfaceAccessor, + PossiblyCurrentContextGlSurfaceAccessor, PossiblyCurrentGlContext, +}; +pub use crate::display::GlDisplay; +pub use crate::surface::GlSurface; diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs new file mode 100644 index 0000000000..29b8b6bb18 --- /dev/null +++ b/glutin/src/surface.rs @@ -0,0 +1,327 @@ +//! A cross platform GL surface representation. + +use std::ffi; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +use raw_window_handle::RawWindowHandle; + +use crate::dispatch_gl; +use crate::display::{Display, GetGlDisplay}; +use crate::error::Result; +use crate::private::Sealed; + +#[cfg(cgl_backend)] +use crate::api::cgl::surface::Surface as CglSurface; +#[cfg(egl_backend)] +use crate::api::egl::surface::Surface as EglSurface; +#[cfg(glx_backend)] +use crate::api::glx::surface::Surface as GlxSurface; +#[cfg(wgl_backend)] +use crate::api::wgl::surface::Surface as WglSurface; + +// 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; + +/// Marker indicating that the surface could be resized. +pub trait ResizeableSurface: Sealed {} + +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; +} + +/// 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 + } +} + +pub trait GlSurface: Sealed { + type SurfaceType: SurfaceTypeTrait; + + /// 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. + fn buffer_age(&self) -> u32; + + /// The width of the underlying surface. + fn width(&self) -> Option; + + /// The height of the underlying surface. + fn height(&self) -> Option; + + /// Check whether the surface is single buffered. + fn is_single_buffered(&self) -> bool; + + /// Swaps the underlying back buffers when the surfate is not single buffered. + fn swap_buffers(&self) -> Result<()>; + + /// Check whether the surface is current on to the curren thread. + fn is_current(&self) -> bool; + + /// Check wherher the surface is current draw surface to the current thread. + fn is_current_draw(&self) -> bool; + + /// Check wherher the surface is current read surface to the current thread. + fn is_current_read(&self) -> bool; + + /// 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. + fn resize(&self, width: NonZeroU32, height: NonZeroU32) + where + Self::SurfaceType: ResizeableSurface; +} + +pub enum Surface { + #[cfg(egl_backend)] + Egl(EglSurface), + + #[cfg(glx_backend)] + Glx(GlxSurface), + + #[cfg(wgl_backend)] + Wgl(WglSurface), + + #[cfg(cgl_backend)] + Cgl(CglSurface), +} + +impl GlSurface for Surface { + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + dispatch_gl!(self; Self(surface) => surface.buffer_age()) + } + + fn width(&self) -> Option { + dispatch_gl!(self; Self(surface) => surface.width()) + } + + fn height(&self) -> Option { + dispatch_gl!(self; Self(surface) => surface.height()) + } + + fn is_single_buffered(&self) -> bool { + dispatch_gl!(self; Self(surface) => surface.is_single_buffered()) + } + + fn swap_buffers(&self) -> Result<()> { + dispatch_gl!(self; Self(surface) => surface.swap_buffers()) + } + + fn is_current(&self) -> bool { + dispatch_gl!(self; Self(surface) => surface.is_current()) + } + + fn is_current_draw(&self) -> bool { + dispatch_gl!(self; Self(surface) => surface.is_current_draw()) + } + + fn is_current_read(&self) -> bool { + dispatch_gl!(self; Self(surface) => surface.is_current_read()) + } + + fn resize(&self, width: NonZeroU32, height: NonZeroU32) + where + Self::SurfaceType: ResizeableSurface, + { + dispatch_gl!(self; Self(surface) => surface.resize(width, height)) + } +} + +impl Sealed for Surface {} +impl Sealed for WindowSurface {} +impl Sealed for PixmapSurface {} +impl Sealed for PbufferSurface {} + +impl GetGlDisplay for Surface { + type Target = Display; + fn display(&self) -> Self::Target { + dispatch_gl!(self; Self(surface) => surface.display(); as Display) + } +} + +impl ResizeableSurface for WindowSurface {} + +// 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 _, + } + } +} + +// Handle to the raw OpenGL surface. +#[derive(Debug, Clone, Copy)] +pub enum RawSurface { + // A pointer to EGLSurface. + #[cfg(egl_backend)] + Egl(*const ffi::c_void), + + /// GLXDrawable. + #[cfg(glx_backend)] + Glx(u64), + + /// TODO + #[cfg(wgl_backend)] + Wgl(*const ffi::c_void), + + /// TODO + #[cfg(cgl_backend)] + Cgl(*const ffi::c_void), +} + +pub trait AsRawSurface { + fn raw_surface(&self) -> RawSurface; +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + dispatch_gl!(self; Self(surface) => surface.raw_surface()) + } +} diff --git a/glutin/src/windowed.rs b/glutin/src/windowed.rs deleted file mode 100644 index f400288e96..0000000000 --- a/glutin/src/windowed.rs +++ /dev/null @@ -1,323 +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() }; -/// # } -/// ``` -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`]. -/// -#[cfg_attr( - target_os = "windows", - doc = "\ -[`platform::windows::RawContextExt`]: crate::platform::windows::RawContextExt -" -)] -#[cfg_attr( - not(target_os = "windows",), - doc = "\ -[`platform::windows::RawContextExt`]: crate::platform -" -)] -#[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\ -[`platform::unix::RawContextExt`]: crate::platform -" -)] -#[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`platform::unix::RawContextExt`]: crate::platform::unix::RawContextExt -" -)] -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`]. -#[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`]. - 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_with_damage()` 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 [`WindowEvent::Resized`] - /// that is received and pass its [`dpi::PhysicalSize`] into this function. - /// - /// [`WindowEvent::Resized`]: winit::event::WindowEvent::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`]. - 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. - /// - /// # A 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 call [`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()`]. - /// - /// [`is_current()`]: Self::is_current() - /// [`make_not_current()`]: Self::make_not_current() - /// [`make_current()`]: Self::make_current() - /// [`treat_as_not_current()`]: Self::treat_as_not_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()`][Self::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()`][Self::make_not_current()] which will do nothing if this - /// context is not current. - /// - /// Please see [`make_current()`][Self::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()`][Self::make_current()] for the preferred method - /// of handling context currency. - 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. - 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/build.rs b/glutin_egl_sys/build.rs index 7b222aaa87..3f1075b29b 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -38,6 +38,7 @@ fn main() { "EGL_KHR_platform_wayland", "EGL_KHR_platform_x11", "EGL_KHR_swap_buffers_with_damage", + "EGL_EXT_pixel_format_float", "EGL_MESA_platform_gbm", ], ); diff --git a/glutin_egl_sys/src/lib.rs b/glutin_egl_sys/src/lib.rs index e6eed6da7d..ef8ce26871 100644 --- a/glutin_egl_sys/src/lib.rs +++ b/glutin_egl_sys/src/lib.rs @@ -7,13 +7,8 @@ target_os = "netbsd", target_os = "openbsd" ))] -#![allow( - clippy::manual_non_exhaustive, - clippy::missing_safety_doc, - clippy::unnecessary_cast, - non_camel_case_types -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![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 d5841f9cbb..a7fa271e1d 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.24" +winit = "0.27.2" +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 77f6fa545e..226c66ebdf 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 1d7df20723..0000000000 --- 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 => (), - 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 8479a469b4..0000000000 --- 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 => (), - 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 7fcaac00aa..0000000000 --- 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().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().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().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 316b97a47f..0000000000 --- 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::ColorType::Rgb8, - ) - .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 0a13168940..0000000000 --- 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 367c22c659..0000000000 --- a/glutin_examples/examples/raw_context.rs +++ /dev/null @@ -1,129 +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, assuming 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 - } - 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 c830aa165a..0000000000 --- a/glutin_examples/examples/sharing.rs +++ /dev/null @@ -1,142 +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 _); - } - - 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 _); - } - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => unsafe { - let _ = ct.get_current(windowed_id).unwrap(); - glw.gl.DeleteFramebuffers(1, &window_fb); - glw.gl.DeleteRenderbuffers(1, &render_buf); - let _ = ct.get_current(headless_id).unwrap(); - glc.gl.DeleteFramebuffers(1, &context_fb); - }, - 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 _); - - 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 _ = ct.get_current(headless_id).unwrap(); - glc.draw_frame([1.0, 0.5, 0.7, 1.0]); - - 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 dfc388dd61..0000000000 --- a/glutin_examples/examples/support/mod.rs +++ /dev/null @@ -1,365 +0,0 @@ -use glutin::{self, PossiblyCurrent}; - -use std::ffi::CStr; - -pub mod gl { - #![allow( - clippy::manual_non_exhaustive, - clippy::too_many_arguments, - clippy::unused_unit, - clippy::upper_case_acronyms, - non_camel_case_types - )] - - pub use self::Gles2 as Gl; - - // gl_bindings.rs is generated in build.rs using https://crates.io/crates/gl_generator - 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: &[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: &[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 df8d26c29a..0000000000 --- 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 => (), - 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 4af3d1543a..4d8d0463a1 100644 --- a/glutin_examples/examples/window.rs +++ b/glutin_examples/examples/window.rs @@ -1,36 +1,105 @@ -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::platform::x11::X11GlConfigExt; +use glutin::prelude::*; +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(true) + .with_surface_type(ConfigSurfaceTypes::WINDOW) + .build(); - let gl = support::load(windowed_context.context()); + 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 = + unsafe { 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).unwrap(); + + 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 => (), 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 654d25f74e..ff8327b746 100644 --- a/glutin_examples/ios-example/rust/src/lib.rs +++ b/glutin_examples/ios-example/rust/src/lib.rs @@ -1,59 +1,4 @@ -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] - -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 => (), - 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 118ff58fb1..2570197140 100644 --- a/glutin_gles2_sys/src/lib.rs +++ b/glutin_gles2_sys/src/lib.rs @@ -1,13 +1,6 @@ #![cfg(target_os = "ios")] -#![allow( - clippy::missing_safety_doc, - clippy::too_many_arguments, - clippy::unused_unit, - non_camel_case_types, - non_snake_case, - non_upper_case_globals -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![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 1ecc40a14c..58bf3d9194 100644 --- a/glutin_glx_sys/src/lib.rs +++ b/glutin_glx_sys/src/lib.rs @@ -5,13 +5,7 @@ target_os = "netbsd", target_os = "openbsd" ))] -#![allow( - clippy::manual_non_exhaustive, - clippy::missing_safety_doc, - clippy::redundant_static_lifetimes, - clippy::unused_unit -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(clippy::all)] pub use self::glx::types::GLXContext; pub use x11_dl::xlib::*; @@ -19,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 751b5cf5ba..0a8d2fdfcb 100644 --- a/glutin_wgl_sys/src/lib.rs +++ b/glutin_wgl_sys/src/lib.rs @@ -1,6 +1,5 @@ #![cfg(any(target_os = "windows"))] -#![allow(clippy::manual_non_exhaustive, clippy::missing_safety_doc, clippy::too_many_arguments)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(clippy::all)] /// WGL bindings pub mod wgl { diff --git a/rustfmt.toml b/rustfmt.toml index 9e91dd035e..f6630ecfb6 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"