├── glutin ├── LICENSE ├── README.md ├── src │ ├── platform │ │ ├── mod.rs │ │ └── x11.rs │ ├── api │ │ ├── mod.rs │ │ ├── cgl │ │ │ ├── mod.rs │ │ │ ├── display.rs │ │ │ ├── surface.rs │ │ │ ├── config.rs │ │ │ └── context.rs │ │ ├── egl │ │ │ ├── mod.rs │ │ │ └── device.rs │ │ ├── wgl │ │ │ ├── mod.rs │ │ │ ├── display.rs │ │ │ └── surface.rs │ │ └── glx │ │ │ ├── mod.rs │ │ │ ├── display.rs │ │ │ ├── surface.rs │ │ │ └── config.rs │ ├── prelude.rs │ ├── lib_loading.rs │ ├── lib.rs │ └── error.rs ├── build.rs └── Cargo.toml ├── glutin_egl_sys ├── LICENSE ├── README.md ├── Cargo.toml ├── src │ ├── lib.rs │ └── egl.rs └── build.rs ├── glutin_examples ├── LICENSE ├── examples │ ├── window.rs │ ├── android.rs │ ├── drm.rs │ ├── egl_device.rs │ └── switch_render_thread.rs ├── Cargo.toml └── build.rs ├── glutin_glx_sys ├── LICENSE ├── README.md ├── Cargo.toml ├── build.rs └── src │ └── lib.rs ├── glutin_wgl_sys ├── LICENSE ├── README.md ├── Cargo.toml ├── src │ └── lib.rs └── build.rs ├── .github ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── _typos.toml ├── glutin-winit ├── README.md ├── Cargo.toml ├── LICENSE ├── build.rs ├── CHANGELOG.md └── src │ ├── event_loop.rs │ ├── window.rs │ └── lib.rs ├── Cargo.toml ├── rustfmt.toml ├── .gitattributes ├── README.md └── LICENSE /glutin/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /glutin/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /glutin_egl_sys/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /glutin_examples/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /glutin_glx_sys/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /glutin_wgl_sys/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: kchibisov 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | .DS_Store 4 | *~ 5 | #*# 6 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [type.md.extend-identifiers] 2 | ApiPrefence = "ApiPrefence" 3 | -------------------------------------------------------------------------------- /glutin_egl_sys/README.md: -------------------------------------------------------------------------------- 1 | # glutin_egl_sys 2 | 3 | Glutin's egl bindings. 4 | -------------------------------------------------------------------------------- /glutin_glx_sys/README.md: -------------------------------------------------------------------------------- 1 | # glutin_glx_sys 2 | 3 | Glutin's glx bindings. 4 | -------------------------------------------------------------------------------- /glutin_wgl_sys/README.md: -------------------------------------------------------------------------------- 1 | # glutin_wgl_sys 2 | 3 | Glutin's wgl bindings. 4 | -------------------------------------------------------------------------------- /glutin/src/platform/mod.rs: -------------------------------------------------------------------------------- 1 | //! Platform-specific API helpers. 2 | 3 | #[cfg(x11_platform)] 4 | pub mod x11; 5 | -------------------------------------------------------------------------------- /glutin_examples/examples/window.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use winit::event_loop::EventLoop; 4 | 5 | fn main() -> Result<(), Box> { 6 | glutin_examples::main(EventLoop::new().unwrap()) 7 | } 8 | -------------------------------------------------------------------------------- /glutin/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | //! The underlying OpenGL platform Api. 2 | 3 | #[cfg(cgl_backend)] 4 | pub mod cgl; 5 | #[cfg(egl_backend)] 6 | pub mod egl; 7 | #[cfg(glx_backend)] 8 | pub mod glx; 9 | #[cfg(wgl_backend)] 10 | pub mod wgl; 11 | -------------------------------------------------------------------------------- /glutin-winit/README.md: -------------------------------------------------------------------------------- 1 | # glutin-winit 2 | 3 | The crate provides cross-platform glutin `Display` bootstrapping with winit. 4 | 5 | This crate is also a reference on how to do the bootstrapping of glutin when 6 | used with a cross-platform windowing library. 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "glutin", 5 | "glutin-winit", 6 | "glutin_examples", 7 | "glutin_egl_sys", 8 | "glutin_glx_sys", 9 | "glutin_wgl_sys", 10 | ] 11 | 12 | [workspace.package] 13 | # MSRV. Also update in ci.yml 14 | rust-version = "1.85.0" 15 | edition = "2024" 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] Tested on all platforms changed 2 | - [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users 3 | - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior 4 | - [ ] Created or updated an example program if it would help users understand this functionality 5 | -------------------------------------------------------------------------------- /glutin_examples/examples/android.rs: -------------------------------------------------------------------------------- 1 | #![cfg(android_platform)] 2 | 3 | use winit::event_loop::EventLoop; 4 | use winit::platform::android::EventLoopBuilderExtAndroid; 5 | 6 | #[unsafe(no_mangle)] 7 | fn android_main(app: winit::platform::android::activity::AndroidApp) { 8 | let event_loop = EventLoop::builder().with_android_app(app).build().unwrap(); 9 | glutin_examples::main(event_loop).unwrap() 10 | } 11 | -------------------------------------------------------------------------------- /glutin_wgl_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin_wgl_sys" 3 | version = "0.6.1" 4 | authors = ["Kirill Chibisov "] 5 | description = "The wgl bindings for glutin" 6 | repository = "https://github.com/rust-windowing/glutin" 7 | license = "Apache-2.0" 8 | readme = "README.md" 9 | rust-version.workspace = true 10 | edition = "2021" 11 | 12 | [build-dependencies] 13 | gl_generator = "0.14" 14 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | format_code_in_doc_comments = true 2 | match_block_trailing_comma = true 3 | condense_wildcard_suffixes = true 4 | use_field_init_shorthand = true 5 | normalize_doc_attributes = true 6 | overflow_delimited_expr = true 7 | imports_granularity = "Module" 8 | use_small_heuristics = "Max" 9 | normalize_comments = true 10 | reorder_impl_items = true 11 | use_try_shorthand = true 12 | newline_style = "Unix" 13 | format_strings = true 14 | wrap_comments = true 15 | comment_width = 80 16 | edition = "2021" 17 | -------------------------------------------------------------------------------- /glutin/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! The glutin prelude. 2 | //! 3 | //! The purpose of this module is to make accessing common imports more 4 | //! convenient. The prelude also imports traits shared by the implementations of 5 | //! graphics apis. 6 | //! 7 | //! ```no_run 8 | //! # #![allow(unused_imports)] 9 | //! use glutin::prelude::*; 10 | //! ``` 11 | 12 | pub use crate::config::GlConfig; 13 | pub use crate::context::{GlContext, NotCurrentGlContext, PossiblyCurrentGlContext}; 14 | pub use crate::display::GlDisplay; 15 | pub use crate::surface::GlSurface; 16 | -------------------------------------------------------------------------------- /glutin_egl_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin_egl_sys" 3 | version = "0.7.1" 4 | authors = ["Kirill Chibisov "] 5 | description = "The egl bindings for glutin" 6 | repository = "https://github.com/rust-windowing/glutin" 7 | license = "Apache-2.0" 8 | readme = "README.md" 9 | rust-version.workspace = true 10 | edition = "2021" 11 | 12 | [build-dependencies] 13 | gl_generator = "0.14" 14 | 15 | [target.'cfg(windows)'.dependencies.windows-sys] 16 | version = "0.52" 17 | features = [ 18 | "Win32_Foundation", 19 | "Win32_Graphics_Gdi", 20 | ] 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /glutin_glx_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin_glx_sys" 3 | version = "0.6.1" 4 | authors = ["Kirill Chibisov "] 5 | description = "The glx bindings for glutin" 6 | repository = "https://github.com/rust-windowing/glutin" 7 | license = "Apache-2.0" 8 | readme = "README.md" 9 | rust-version.workspace = true 10 | edition = "2021" 11 | 12 | [build-dependencies] 13 | gl_generator = "0.14" 14 | 15 | [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="netbsd", target_os="openbsd"))'.dependencies] 16 | x11-dl = "2.18.3" 17 | -------------------------------------------------------------------------------- /glutin_wgl_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(windows)] 2 | #![allow(clippy::too_many_arguments)] 3 | #![allow(clippy::missing_safety_doc)] 4 | #![allow(clippy::manual_non_exhaustive)] 5 | #![allow(clippy::unnecessary_cast)] 6 | #![allow(unused_imports)] 7 | #![allow(unknown_lints, clippy::missing_transmute_annotations)] 8 | 9 | /// WGL bindings 10 | pub mod wgl { 11 | include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs")); 12 | } 13 | 14 | /// Functions that are not necessarily always available 15 | pub mod wgl_extra { 16 | include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs")); 17 | } 18 | 19 | #[link(name = "opengl32")] 20 | extern "C" {} 21 | -------------------------------------------------------------------------------- /glutin-winit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin-winit" 3 | version = "0.5.0" 4 | authors = ["Kirill Chibisov "] 5 | description = "Glutin bootstrapping helpers with winit" 6 | keywords = ["windowing", "opengl", "winit"] 7 | license = "MIT" 8 | readme = "README.md" 9 | repository = "https://github.com/rust-windowing/glutin" 10 | rust-version.workspace = true 11 | edition.workspace = true 12 | 13 | [features] 14 | default = ["egl", "glx", "x11", "wayland", "wgl"] 15 | egl = ["glutin/egl"] 16 | glx = ["glutin/glx", "x11"] 17 | wgl = ["glutin/wgl"] 18 | x11 = ["glutin/x11", "winit/x11"] 19 | wayland = ["glutin/wayland", "winit/wayland"] 20 | 21 | [dependencies] 22 | glutin = { version = "0.32.0", path = "../glutin", default-features = false } 23 | raw-window-handle = "0.6" 24 | winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } 25 | 26 | [build-dependencies] 27 | cfg_aliases = "0.2.1" 28 | -------------------------------------------------------------------------------- /glutin-winit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Kirill Chibisov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the “Software”), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /glutin-winit/build.rs: -------------------------------------------------------------------------------- 1 | // XXX keep in sync with glutin's build.rs. 2 | 3 | use cfg_aliases::cfg_aliases; 4 | 5 | fn main() { 6 | // Setup alias to reduce `cfg` boilerplate. 7 | cfg_aliases! { 8 | // Systems. 9 | android_platform: { target_os = "android" }, 10 | wasm_platform: { target_family = "wasm" }, 11 | macos_platform: { target_os = "macos" }, 12 | ios_platform: { target_os = "ios" }, 13 | apple: { any(ios_platform, macos_platform) }, 14 | free_unix: { all(unix, not(apple), not(android_platform)) }, 15 | 16 | // Native displays. 17 | x11_platform: { all(feature = "x11", free_unix, not(wasm_platform)) }, 18 | wayland_platform: { all(feature = "wayland", free_unix, not(wasm_platform)) }, 19 | 20 | // Backends. 21 | egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm_platform)) }, 22 | glx_backend: { all(feature = "glx", x11_platform, not(wasm_platform)) }, 23 | wgl_backend: { all(feature = "wgl", windows, not(wasm_platform)) }, 24 | cgl_backend: { all(macos_platform, not(wasm_platform)) }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /glutin/build.rs: -------------------------------------------------------------------------------- 1 | use cfg_aliases::cfg_aliases; 2 | 3 | fn main() { 4 | // Setup alias to reduce `cfg` boilerplate. 5 | cfg_aliases! { 6 | // Systems. 7 | android_platform: { target_os = "android" }, 8 | ohos_platform: { target_env = "ohos" }, 9 | wasm_platform: { target_family = "wasm" }, 10 | macos_platform: { target_os = "macos" }, 11 | ios_platform: { target_os = "ios" }, 12 | apple: { any(ios_platform, macos_platform) }, 13 | free_unix: { all(unix, not(apple), not(android_platform), not(ohos_platform)) }, 14 | 15 | // Native displays. 16 | x11_platform: { all(feature = "x11", free_unix, not(wasm_platform)) }, 17 | wayland_platform: { all(feature = "wayland", free_unix, not(wasm_platform)) }, 18 | 19 | // Backends. 20 | egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm_platform)) }, 21 | glx_backend: { all(feature = "glx", x11_platform, not(wasm_platform)) }, 22 | wgl_backend: { all(feature = "wgl", windows, not(wasm_platform)) }, 23 | cgl_backend: { all(macos_platform, not(wasm_platform)) }, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /glutin_examples/examples/drm.rs: -------------------------------------------------------------------------------- 1 | use std::fs::OpenOptions; 2 | use std::os::fd::AsRawFd; 3 | 4 | use glutin::api::egl; 5 | use raw_window_handle::{DrmDisplayHandle, RawDisplayHandle}; 6 | 7 | fn main() { 8 | let devices = egl::device::Device::query_devices().expect("Query EGL devices"); 9 | for egl_device in devices { 10 | dbg!(&egl_device); 11 | dbg!(egl_device.drm_render_device_node_path()); 12 | let Some(drm) = dbg!(egl_device.drm_device_node_path()) else { 13 | continue; 14 | }; 15 | let fd = OpenOptions::new() 16 | .read(true) 17 | .write(true) 18 | .open(drm) 19 | .expect("Open DRM device with Read/Write permissions"); 20 | 21 | // https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm.txt: 22 | // Providing DRM_MASTER_FD is only to cover cases where EGL might fail to open 23 | // it itself. 24 | let rdh = RawDisplayHandle::Drm(DrmDisplayHandle::new(fd.as_raw_fd())); 25 | 26 | let egl_display = unsafe { egl::display::Display::with_device(&egl_device, Some(rdh)) } 27 | .expect("Create EGL Display"); 28 | dbg!(&egl_display); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /glutin_egl_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(any( 2 | windows, 3 | target_os = "linux", 4 | target_os = "android", 5 | target_os = "dragonfly", 6 | target_os = "freebsd", 7 | target_os = "netbsd", 8 | target_os = "openbsd" 9 | ))] 10 | #![allow(non_camel_case_types)] 11 | #![allow(clippy::missing_safety_doc)] 12 | #![allow(clippy::manual_non_exhaustive)] 13 | #![allow(clippy::unnecessary_cast)] 14 | #![allow(unknown_lints, clippy::missing_transmute_annotations)] 15 | 16 | pub mod egl; 17 | 18 | pub use self::egl::types::{EGLContext, EGLDisplay}; 19 | 20 | use std::os::raw; 21 | 22 | pub type khronos_utime_nanoseconds_t = khronos_uint64_t; 23 | pub type khronos_uint64_t = u64; 24 | pub type khronos_ssize_t = raw::c_long; 25 | pub type EGLint = i32; 26 | pub type EGLenum = raw::c_uint; 27 | pub type EGLNativeDisplayType = *const raw::c_void; 28 | 29 | // FIXME: egl_native_pixmap_t instead 30 | #[cfg(windows)] 31 | pub type EGLNativePixmapType = windows_sys::Win32::Graphics::Gdi::HBITMAP; 32 | #[cfg(not(windows))] 33 | pub type EGLNativePixmapType = *const raw::c_void; 34 | 35 | #[cfg(windows)] 36 | pub type EGLNativeWindowType = windows_sys::Win32::Foundation::HWND; 37 | #[cfg(not(windows))] 38 | pub type EGLNativeWindowType = *const raw::c_void; 39 | -------------------------------------------------------------------------------- /glutin-winit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | # Version 0.5.0 4 | 5 | - **Breaking:** Update _winit_ to `0.30`. See [winit's CHANGELOG](https://github.com/rust-windowing/winit/releases/tag/v0.30.0) for more info. 6 | - Add the `GlutinEventLoop` trait to maintain compatibility with the now 7 | deprecated `EventLoop` but also support the new `ActiveEventLoop`. 8 | - Update `DisplayBuilder` to use `WindowAttributes` instead of `WindowBuilder`. 9 | 10 | # Version 0.4.2 11 | 12 | - **Breaking:** Update _glutin_ to `0.31.0`. See [glutin's CHANGELOG](https://github.com/rust-windowing/glutin/releases/tag/v0.31.0) for more info. 13 | - **Breaking:** Update _winit_ to `0.29.2`. See [winit's CHANGELOG](https://github.com/rust-windowing/winit/releases/tag/v0.29.2) for more info. 14 | - **Breaking:** Fixed a typo in a type name (`ApiPrefence` -> `ApiPreference`). 15 | 16 | # Version 0.3.0 17 | 18 | - **Breaking:** Update _winit_ to `0.28`. See [winit's CHANGELOG](https://github.com/rust-windowing/winit/releases/tag/v0.28.0) for more info. 19 | 20 | # Version 0.2.2 21 | 22 | - Add traits `GlWindow` with helper methods for building and resizing surfaces using a winit `Window`. 23 | 24 | # Version 0.2.1 25 | 26 | - Fix WGL window initialization. 27 | 28 | # Version 0.2.0 29 | 30 | - Fix API typo. 31 | 32 | # Version 0.1.0 33 | 34 | - Implement _glutin-winit_ helpers. 35 | -------------------------------------------------------------------------------- /glutin_examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin_examples" 3 | version = "0.1.3" 4 | authors = ["Kirill Chibisov "] 5 | description = "Examples for glutin" 6 | repository = "https://github.com/rust-windowing/glutin" 7 | license = "Apache-2.0" 8 | readme = "../README.md" 9 | rust-version.workspace = true 10 | edition.workspace = true 11 | publish = false 12 | 13 | [features] 14 | default = ["egl", "glx", "x11", "wayland", "wgl"] 15 | egl = ["glutin-winit/egl", "png"] 16 | glx = ["glutin-winit/glx"] 17 | wgl = ["glutin-winit/wgl"] 18 | x11 = ["glutin-winit/x11"] 19 | wayland = ["glutin-winit/wayland", "winit/wayland-dlopen", "winit/wayland-csd-adwaita"] 20 | 21 | [dependencies] 22 | glutin = { path = "../glutin", default-features = false } 23 | glutin-winit = { path = "../glutin-winit", default-features = false } 24 | png = { version = "0.17.6", optional = true } 25 | raw-window-handle = "0.6" 26 | winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } 27 | drm = { version = "0.12", optional = true } 28 | 29 | [target.'cfg(target_os = "android")'.dependencies] 30 | winit = { version = "0.30.0", default-features = false, features = ["android-native-activity", "rwh_06"] } 31 | 32 | [build-dependencies] 33 | gl_generator = "0.14" 34 | cfg_aliases = "0.2.1" 35 | 36 | [[example]] 37 | name = "android" 38 | crate-type = ["cdylib"] 39 | 40 | [[example]] 41 | name = "egl_device" 42 | required-features = ["egl"] 43 | 44 | [[example]] 45 | name = "drm" 46 | required-features = ["egl", "drm"] 47 | -------------------------------------------------------------------------------- /glutin/src/lib_loading.rs: -------------------------------------------------------------------------------- 1 | //! Library loading routines. 2 | 3 | use std::ops::{Deref, DerefMut}; 4 | use std::sync::Arc; 5 | 6 | use libloading::Library; 7 | 8 | #[cfg(windows)] 9 | use libloading::os::windows::{LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, Library as WinLibrary}; 10 | 11 | pub trait SymLoading { 12 | /// # Safety 13 | /// The library must be unsured to live long enough. 14 | unsafe fn load_with(lib: &Library) -> Self; 15 | } 16 | 17 | #[derive(Clone)] 18 | #[allow(missing_debug_implementations)] 19 | pub struct SymWrapper { 20 | sym: T, 21 | _lib: Arc, 22 | } 23 | 24 | impl SymWrapper { 25 | pub unsafe fn new(lib_paths: &[&str]) -> Result { 26 | unsafe { 27 | for path in lib_paths { 28 | #[cfg(windows)] 29 | let lib = WinLibrary::load_with_flags(path, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) 30 | .map(From::from); 31 | 32 | #[cfg(not(windows))] 33 | let lib = Library::new(path); 34 | 35 | if let Ok(lib) = lib { 36 | return Ok(SymWrapper { sym: T::load_with(&lib), _lib: Arc::new(lib) }); 37 | } 38 | } 39 | } 40 | 41 | Err(()) 42 | } 43 | } 44 | 45 | impl Deref for SymWrapper { 46 | type Target = T; 47 | 48 | fn deref(&self) -> &Self::Target { 49 | &self.sym 50 | } 51 | } 52 | 53 | impl DerefMut for SymWrapper { 54 | fn deref_mut(&mut self) -> &mut Self::Target { 55 | &mut self.sym 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /glutin_wgl_sys/build.rs: -------------------------------------------------------------------------------- 1 | use gl_generator::{Api, Fallbacks, Profile, Registry}; 2 | use std::env; 3 | use std::fs::File; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let target = env::var("TARGET").unwrap(); 8 | let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | 12 | if target.contains("windows") { 13 | let mut file = File::create(dest.join("wgl_bindings.rs")).unwrap(); 14 | Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, []) 15 | .write_bindings(gl_generator::StaticGenerator, &mut file) 16 | .unwrap(); 17 | 18 | let mut file = File::create(dest.join("wgl_extra_bindings.rs")).unwrap(); 19 | Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [ 20 | "WGL_ARB_context_flush_control", 21 | "WGL_ARB_create_context_no_error", 22 | "WGL_ARB_create_context_profile", 23 | "WGL_ARB_create_context_robustness", 24 | "WGL_ARB_create_context", 25 | "WGL_ARB_extensions_string", 26 | "WGL_ARB_framebuffer_sRGB", 27 | "WGL_ARB_pbuffer", 28 | "WGL_ARB_multisample", 29 | "WGL_ARB_pixel_format", 30 | "WGL_ARB_pixel_format_float", 31 | "WGL_EXT_create_context_es2_profile", 32 | "WGL_EXT_extensions_string", 33 | "WGL_EXT_framebuffer_sRGB", 34 | "WGL_EXT_swap_control", 35 | ]) 36 | .write_bindings(gl_generator::StructGenerator, &mut file) 37 | .unwrap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /glutin_examples/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::path::PathBuf; 4 | 5 | use cfg_aliases::cfg_aliases; 6 | use gl_generator::{Api, Fallbacks, Profile, Registry, StructGenerator}; 7 | 8 | fn main() { 9 | // XXX this is taken from glutin/build.rs. 10 | 11 | // Setup alias to reduce `cfg` boilerplate. 12 | cfg_aliases! { 13 | // Systems. 14 | android_platform: { target_os = "android" }, 15 | wasm_platform: { target_family = "wasm" }, 16 | macos_platform: { target_os = "macos" }, 17 | ios_platform: { target_os = "ios" }, 18 | apple: { any(ios_platform, macos_platform) }, 19 | free_unix: { all(unix, not(apple), not(android_platform)) }, 20 | 21 | // Native displays. 22 | x11_platform: { all(feature = "x11", free_unix, not(wasm_platform)) }, 23 | wayland_platform: { all(feature = "wayland", free_unix, not(wasm_platform)) }, 24 | 25 | // Backends. 26 | egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm_platform)) }, 27 | glx_backend: { all(feature = "glx", x11_platform, not(wasm_platform)) }, 28 | wgl_backend: { all(feature = "wgl", windows, not(wasm_platform)) }, 29 | cgl_backend: { all(macos_platform, not(wasm_platform)) }, 30 | } 31 | 32 | let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); 33 | 34 | println!("cargo:rerun-if-changed=build.rs"); 35 | 36 | let mut file = File::create(dest.join("gl_bindings.rs")).unwrap(); 37 | Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, []) 38 | .write_bindings(StructGenerator, &mut file) 39 | .unwrap(); 40 | } 41 | -------------------------------------------------------------------------------- /glutin_glx_sys/build.rs: -------------------------------------------------------------------------------- 1 | use gl_generator::{Api, Fallbacks, Profile, Registry}; 2 | use std::env; 3 | use std::fs::File; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let target = env::var("TARGET").unwrap(); 8 | let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | 12 | if target.contains("linux") 13 | || target.contains("dragonfly") 14 | || target.contains("freebsd") 15 | || target.contains("netbsd") 16 | || target.contains("openbsd") 17 | { 18 | let mut file = File::create(dest.join("glx_bindings.rs")).unwrap(); 19 | Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, []) 20 | .write_bindings(gl_generator::StructGenerator, &mut file) 21 | .unwrap(); 22 | 23 | let mut file = File::create(dest.join("glx_extra_bindings.rs")).unwrap(); 24 | Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, [ 25 | "GLX_ARB_context_flush_control", 26 | "GLX_ARB_create_context", 27 | "GLX_ARB_create_context_no_error", 28 | "GLX_ARB_create_context_profile", 29 | "GLX_ARB_create_context_robustness", 30 | "GLX_ARB_fbconfig_float", 31 | "GLX_ARB_framebuffer_sRGB", 32 | "GLX_ARB_multisample", 33 | "GLX_EXT_buffer_age", 34 | "GLX_EXT_create_context_es2_profile", 35 | "GLX_EXT_framebuffer_sRGB", 36 | "GLX_EXT_swap_control", 37 | "GLX_MESA_swap_control", 38 | "GLX_SGI_swap_control", 39 | ]) 40 | .write_bindings(gl_generator::StructGenerator, &mut file) 41 | .unwrap(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /glutin-winit/src/event_loop.rs: -------------------------------------------------------------------------------- 1 | use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle}; 2 | use winit::error::OsError; 3 | use winit::event_loop::{ActiveEventLoop, EventLoop}; 4 | use winit::window::{Window, WindowAttributes}; 5 | 6 | use crate::private::Sealed; 7 | 8 | /// [`ActiveEventLoop`] is the recommended way to interact with the event 9 | /// loop, but for compatibility purposes [`EventLoop`] is also supported 10 | /// although not recommended anymore as it has been deprecated by Winit. 11 | pub trait GlutinEventLoop: Sealed { 12 | /// Create the window. 13 | /// 14 | /// See [`ActiveEventLoop::create_window`] for details. 15 | fn create_window(&self, window_attributes: WindowAttributes) -> Result; 16 | 17 | /// Get a handle to the display controller of the windowing system. 18 | fn glutin_display_handle(&self) -> Result, HandleError>; 19 | } 20 | 21 | impl Sealed for ActiveEventLoop {} 22 | 23 | impl GlutinEventLoop for ActiveEventLoop { 24 | fn create_window(&self, window_attributes: WindowAttributes) -> Result { 25 | self.create_window(window_attributes) 26 | } 27 | 28 | fn glutin_display_handle(&self) -> Result, HandleError> { 29 | self.display_handle() 30 | } 31 | } 32 | 33 | impl Sealed for EventLoop {} 34 | 35 | impl GlutinEventLoop for EventLoop { 36 | #[allow(deprecated)] 37 | fn create_window(&self, window_attributes: WindowAttributes) -> Result { 38 | self.create_window(window_attributes) 39 | } 40 | 41 | fn glutin_display_handle(&self) -> Result, HandleError> { 42 | self.display_handle() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /glutin_glx_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(any( 2 | target_os = "linux", 3 | target_os = "dragonfly", 4 | target_os = "freebsd", 5 | target_os = "netbsd", 6 | target_os = "openbsd" 7 | ))] 8 | #![allow(clippy::missing_safety_doc)] 9 | #![allow(clippy::manual_non_exhaustive)] 10 | #![allow(clippy::unused_unit)] 11 | #![allow(clippy::redundant_static_lifetimes)] 12 | #![allow(clippy::unnecessary_cast)] 13 | #![allow(unknown_lints, clippy::missing_transmute_annotations)] 14 | 15 | pub use self::glx::types::GLXContext; 16 | pub use x11_dl::xlib::*; 17 | 18 | /// GLX bindings 19 | pub mod glx { 20 | include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs")); 21 | 22 | // The GLX protocol error codes extracted from . 23 | pub const PROTO_BAD_CONTEXT: types::GLenum = 0; 24 | pub const PROTO_BAD_CONTEXT_STATE: types::GLenum = 1; 25 | pub const PROTO_BAD_DRAWABLE: types::GLenum = 2; 26 | pub const PROTO_BAD_PIXMAP: types::GLenum = 3; 27 | pub const PROTO_BAD_CONTEXT_TAG: types::GLenum = 4; 28 | pub const PROTO_BAD_CURRENT_WINDOW: types::GLenum = 5; 29 | pub const PROTO_BAD_RENDER_REQUEST: types::GLenum = 6; 30 | pub const PROTO_BAD_LARGE_REQUEST: types::GLenum = 7; 31 | pub const PROTO_UNSUPPORTED_PRIVATE_REQUEST: types::GLenum = 8; 32 | pub const PROTO_BAD_FBCONFIG: types::GLenum = 9; 33 | pub const PROTO_BAD_PBUFFER: types::GLenum = 10; 34 | pub const PROTO_BAD_CURRENT_DRAWABLE: types::GLenum = 11; 35 | pub const PROTO_BAD_WINDOW: types::GLenum = 12; 36 | pub const PROTO_BAD_PROFILE_ARB: types::GLenum = 13; 37 | } 38 | 39 | /// Functions that are not necessarily always available 40 | pub mod glx_extra { 41 | include!(concat!(env!("OUT_DIR"), "/glx_extra_bindings.rs")); 42 | } 43 | -------------------------------------------------------------------------------- /glutin/src/api/cgl/mod.rs: -------------------------------------------------------------------------------- 1 | //! The CGL Api. 2 | 3 | #![allow(non_upper_case_globals)] 4 | #![allow(clippy::let_unit_value)] // Temporary 5 | 6 | use std::ffi::CStr; 7 | 8 | #[allow(deprecated)] 9 | use objc2_open_gl::{CGLError, CGLErrorString}; 10 | 11 | use crate::error::{Error, ErrorKind, Result}; 12 | 13 | pub mod config; 14 | pub mod context; 15 | pub mod display; 16 | pub mod surface; 17 | 18 | #[allow(deprecated)] 19 | pub(crate) fn check_error(error: CGLError) -> Result<()> { 20 | let kind = match error { 21 | CGLError::NoError => return Ok(()), 22 | CGLError::BadAttribute => ErrorKind::BadAttribute, 23 | CGLError::BadProperty => ErrorKind::BadParameter, 24 | CGLError::BadPixelFormat => ErrorKind::BadConfig, 25 | CGLError::BadContext => ErrorKind::BadContext, 26 | CGLError::BadDrawable => ErrorKind::BadSurface, 27 | CGLError::BadDisplay => ErrorKind::BadDisplay, 28 | CGLError::BadState => ErrorKind::BadContextState, 29 | CGLError::BadValue => ErrorKind::BadAttribute, 30 | CGLError::BadEnumeration => ErrorKind::BadAttribute, 31 | CGLError::BadOffScreen => ErrorKind::BadSurface, 32 | CGLError::BadMatch => ErrorKind::BadMatch, 33 | CGLError::BadWindow => ErrorKind::BadNativeWindow, 34 | CGLError::BadAddress => ErrorKind::BadAccess, 35 | CGLError::BadAlloc => ErrorKind::OutOfMemory, 36 | CGLError::BadCodeModule 37 | | CGLError::BadConnection 38 | | CGLError::BadRendererInfo 39 | | CGLError::BadFullScreen => ErrorKind::Misc, 40 | _ => ErrorKind::Misc, 41 | }; 42 | 43 | let description = unsafe { 44 | CStr::from_ptr(CGLErrorString(error).as_ptr()).to_str().unwrap_or_default().to_string() 45 | }; 46 | Err(Error::new(Some(error.0 as _), Some(description), kind)) 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glutin - OpenGL, UTilities, and INput 2 | 3 | A low-level library for OpenGL context creation. 4 | 5 | [![](https://img.shields.io/crates/v/glutin.svg)](https://crates.io/crates/glutin) 6 | [![Docs.rs](https://docs.rs/glutin/badge.svg)](https://docs.rs/glutin) 7 | 8 | ## [Documentation](https://docs.rs/glutin) 9 | 10 | ## Contact Us 11 | 12 | Join us in any of these: 13 | 14 | [![Matrix](https://img.shields.io/badge/Matrix-%23winit%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#winit:matrix.org) 15 | [![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit) 16 | 17 | ## Usage Examples 18 | 19 | **Warning:** These are examples for `master`. You can find examples for 20 | the latest _released version_ [here](https://github.com/rust-windowing/glutin/releases/latest). 21 | 22 | The examples use [`gl_generator`](https://crates.io/crates/gl_generator) to 23 | generate OpenGL bindings. 24 | 25 | ### Try it! 26 | 27 | ```bash 28 | git clone https://github.com/rust-windowing/glutin 29 | cd glutin 30 | cargo run --example window 31 | ``` 32 | 33 | ### Usage 34 | 35 | Glutin is an OpenGL context creation library, and doesn't directly provide 36 | OpenGL bindings for you. 37 | 38 | For examples, please look [here](https://github.com/rust-windowing/glutin/tree/master/glutin_examples). 39 | 40 | Note that glutin aims at being a low-level brick in your rendering 41 | infrastructure. You are encouraged to write another layer of abstraction 42 | between glutin and your application. 43 | 44 | Glutin follows winit's [MSRV policy](https://github.com/rust-windowing/winit/blob/master/README.md#msrv-policy). 45 | 46 | ## Platform-specific notes 47 | 48 | ### Android 49 | 50 | Be sure to handle Android's lifecycle correctly when using a `winit` window 51 | by only creating a GL surface after `winit` raises `Event::Resumed`, and 52 | destroy it again upon receiving `Event::Suspended`. See this in action in the 53 | [`android.rs` example](./glutin_examples/examples/android.rs). 54 | 55 | To compile and run the Android example on your device, 56 | install [`cargo-apk`](https://crates.io/crates/cargo-apk) 57 | and start the app using: 58 | 59 | ```console 60 | $ cargo apk r -p glutin_examples --example android 61 | ``` 62 | -------------------------------------------------------------------------------- /glutin_egl_sys/build.rs: -------------------------------------------------------------------------------- 1 | use gl_generator::{Api, Fallbacks, Profile, Registry}; 2 | use std::env; 3 | use std::fs::File; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let target = env::var("TARGET").unwrap(); 8 | let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | 12 | if target.contains("linux") 13 | || target.contains("dragonfly") 14 | || target.contains("freebsd") 15 | || target.contains("netbsd") 16 | || target.contains("openbsd") 17 | || target.contains("windows") 18 | || target.contains("android") 19 | || target.contains("ios") 20 | { 21 | let mut file = File::create(dest.join("egl_bindings.rs")).unwrap(); 22 | let reg = Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ 23 | "EGL_ANDROID_native_fence_sync", 24 | "EGL_EXT_buffer_age", 25 | "EGL_EXT_create_context_robustness", 26 | "EGL_EXT_device_base", 27 | "EGL_EXT_device_drm", 28 | "EGL_EXT_device_drm_render_node", 29 | "EGL_EXT_device_enumeration", 30 | "EGL_EXT_device_query", 31 | "EGL_EXT_device_query_name", 32 | "EGL_EXT_pixel_format_float", 33 | "EGL_EXT_platform_base", 34 | "EGL_EXT_platform_device", 35 | "EGL_EXT_platform_wayland", 36 | "EGL_EXT_platform_x11", 37 | "EGL_EXT_swap_buffers_with_damage", 38 | "EGL_IMG_context_priority", 39 | "EGL_KHR_create_context", 40 | "EGL_KHR_create_context_no_error", 41 | "EGL_KHR_display_reference", 42 | "EGL_KHR_fence_sync", 43 | "EGL_KHR_image_base", 44 | "EGL_KHR_platform_android", 45 | "EGL_KHR_platform_gbm", 46 | "EGL_KHR_platform_wayland", 47 | "EGL_KHR_platform_x11", 48 | "EGL_KHR_swap_buffers_with_damage", 49 | "EGL_KHR_wait_sync", 50 | "EGL_MESA_platform_gbm", 51 | "EGL_NV_context_priority_realtime", 52 | ]); 53 | 54 | if target.contains("ios") { 55 | reg.write_bindings(gl_generator::StaticStructGenerator, &mut file) 56 | } else { 57 | reg.write_bindings(gl_generator::StructGenerator, &mut file) 58 | } 59 | .unwrap() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /glutin-winit/src/window.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroU32; 2 | 3 | use glutin::context::PossiblyCurrentContext; 4 | use glutin::surface::{ 5 | GlSurface, ResizeableSurface, Surface, SurfaceAttributes, SurfaceAttributesBuilder, 6 | SurfaceTypeTrait, WindowSurface, 7 | }; 8 | use raw_window_handle::{HandleError, HasWindowHandle}; 9 | use winit::window::Window; 10 | 11 | /// [`Window`] extensions for working with [`glutin`] surfaces. 12 | pub trait GlWindow { 13 | /// Build the surface attributes suitable to create a window surface. 14 | /// 15 | /// # Panics 16 | /// Panics if either window inner dimension is zero. 17 | /// 18 | /// # Example 19 | /// ```no_run 20 | /// use glutin_winit::GlWindow; 21 | /// # let winit_window: winit::window::Window = unimplemented!(); 22 | /// 23 | /// let attrs = winit_window.build_surface_attributes(<_>::default()); 24 | /// ``` 25 | fn build_surface_attributes( 26 | &self, 27 | builder: SurfaceAttributesBuilder, 28 | ) -> Result, HandleError>; 29 | 30 | /// Resize the surface to the window inner size. 31 | /// 32 | /// No-op if either window size is zero. 33 | /// 34 | /// # Example 35 | /// ```no_run 36 | /// use glutin_winit::GlWindow; 37 | /// # use glutin::surface::{Surface, WindowSurface}; 38 | /// # let winit_window: winit::window::Window = unimplemented!(); 39 | /// # let (gl_surface, gl_context): (Surface, _) = unimplemented!(); 40 | /// 41 | /// winit_window.resize_surface(&gl_surface, &gl_context); 42 | /// ``` 43 | fn resize_surface( 44 | &self, 45 | surface: &Surface, 46 | context: &PossiblyCurrentContext, 47 | ); 48 | } 49 | 50 | impl GlWindow for Window { 51 | fn build_surface_attributes( 52 | &self, 53 | builder: SurfaceAttributesBuilder, 54 | ) -> Result, HandleError> { 55 | let (w, h) = self.inner_size().non_zero().expect("invalid zero inner size"); 56 | let handle = self.window_handle()?.as_raw(); 57 | Ok(builder.build(handle, w, h)) 58 | } 59 | 60 | fn resize_surface( 61 | &self, 62 | surface: &Surface, 63 | context: &PossiblyCurrentContext, 64 | ) { 65 | if let Some((w, h)) = self.inner_size().non_zero() { 66 | surface.resize(context, w, h) 67 | } 68 | } 69 | } 70 | 71 | /// [`winit::dpi::PhysicalSize`] non-zero extensions. 72 | trait NonZeroU32PhysicalSize { 73 | /// Converts to non-zero `(width, height)`. 74 | fn non_zero(self) -> Option<(NonZeroU32, NonZeroU32)>; 75 | } 76 | impl NonZeroU32PhysicalSize for winit::dpi::PhysicalSize { 77 | fn non_zero(self) -> Option<(NonZeroU32, NonZeroU32)> { 78 | let w = NonZeroU32::new(self.width)?; 79 | let h = NonZeroU32::new(self.height)?; 80 | Some((w, h)) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /glutin/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The purpose of this library is to provide an OpenGL [`context`] for as many 2 | //! platforms as possible, abstracting away the underlying differences without 3 | //! losing access to platform specific extensions. 4 | //! 5 | //! However Glutin doesn't force users into using the cross platform 6 | //! abstractions. When only a particular [`api`] is desired, it can 7 | //! be used directly. 8 | //! 9 | //! The initialization starts by loading and connecting to the platform's 10 | //! graphics Api when creating a [`display`]. This object is used to create all 11 | //! the OpenGL objects, such as [`config`], [`context`], and [`surface`]. 12 | //! 13 | //! ## Environment variables 14 | //! 15 | //! `GLUTIN_WGL_OPENGL_DLL` - change the name of the OpenGL DLL to load. 16 | 17 | #![deny(rust_2018_idioms)] 18 | #![deny(rustdoc::broken_intra_doc_links)] 19 | #![deny(improper_ctypes, improper_ctypes_definitions)] 20 | #![deny(clippy::all)] 21 | #![deny(missing_debug_implementations)] 22 | #![deny(missing_docs)] 23 | #![cfg_attr(clippy, deny(warnings))] 24 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 25 | 26 | #[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] 27 | compile_error!("Please select at least one api backend"); 28 | 29 | pub mod api; 30 | pub mod config; 31 | pub mod context; 32 | pub mod display; 33 | pub mod error; 34 | pub mod platform; 35 | pub mod prelude; 36 | pub mod surface; 37 | 38 | #[cfg(any(egl_backend, glx_backend))] 39 | mod lib_loading; 40 | 41 | pub(crate) mod private { 42 | /// Prevent traits from being implemented downstream, since those are used 43 | /// purely for documentation organization and simplify platform api 44 | /// implementation maintenance. 45 | pub trait Sealed {} 46 | 47 | /// `gl_api_dispatch!(match expr; Enum(foo) => foo.something())` 48 | /// expands to the equivalent of 49 | /// ```ignore 50 | /// match self { 51 | /// Enum::Egl(foo) => foo.something(), 52 | /// Enum::Glx(foo) => foo.something(), 53 | /// Enum::Wgl(foo) => foo.something(), 54 | /// Enum::Cgl(foo) => foo.something(), 55 | /// } 56 | /// ``` 57 | /// The result can be converted to another enum by adding `; as AnotherEnum` 58 | macro_rules! gl_api_dispatch { 59 | ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => { 60 | match $what { 61 | #[cfg(egl_backend)] 62 | $enum::Egl($($c1)*) => $enum2::Egl($x), 63 | #[cfg(glx_backend)] 64 | $enum::Glx($($c1)*) => $enum2::Glx($x), 65 | #[cfg(wgl_backend)] 66 | $enum::Wgl($($c1)*) => $enum2::Wgl($x), 67 | #[cfg(cgl_backend)] 68 | $enum::Cgl($($c1)*) => $enum2::Cgl($x), 69 | } 70 | }; 71 | ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr) => { 72 | match $what { 73 | #[cfg(egl_backend)] 74 | $enum::Egl($($c1)*) => $x, 75 | #[cfg(glx_backend)] 76 | $enum::Glx($($c1)*) => $x, 77 | #[cfg(wgl_backend)] 78 | $enum::Wgl($($c1)*) => $x, 79 | #[cfg(cgl_backend)] 80 | $enum::Cgl($($c1)*) => $x, 81 | } 82 | }; 83 | } 84 | 85 | pub(crate) use gl_api_dispatch; 86 | } 87 | -------------------------------------------------------------------------------- /glutin/src/platform/x11.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to access X11 specific config properties. 2 | 3 | use std::mem; 4 | 5 | use once_cell::sync::Lazy; 6 | use x11_dl::xlib::{Display, XVisualInfo, Xlib}; 7 | #[cfg(egl_backend)] 8 | use x11_dl::xlib::{VisualIDMask, XID}; 9 | use x11_dl::xrender::Xrender; 10 | 11 | /// The XLIB handle. 12 | pub(crate) static XLIB: Lazy> = Lazy::new(|| Xlib::open().ok()); 13 | 14 | /// The XRENDER handle. 15 | static XRENDER: Lazy> = Lazy::new(|| Xrender::open().ok()); 16 | 17 | /// The GlConfig extension trait to get X11 specific properties from a config. 18 | pub trait X11GlConfigExt { 19 | /// The `X11VisualInfo` that must be used to initialize the Xlib window. 20 | fn x11_visual(&self) -> Option; 21 | } 22 | 23 | /// The X11 visual info. 24 | /// 25 | /// This must be used when building X11 window, so it'll be compatible with the 26 | /// underlying Api. 27 | #[derive(Debug)] 28 | pub struct X11VisualInfo { 29 | raw: *const XVisualInfo, 30 | transparency: bool, 31 | } 32 | 33 | impl X11VisualInfo { 34 | #[cfg(egl_backend)] 35 | pub(crate) unsafe fn from_xid(display: *mut Display, xid: XID) -> Option { 36 | let xlib = XLIB.as_ref().unwrap(); 37 | 38 | if xid == 0 { 39 | return None; 40 | } 41 | 42 | let raw = unsafe { 43 | let mut raw: XVisualInfo = std::mem::zeroed(); 44 | raw.visualid = xid; 45 | 46 | let mut num_visuals = 0; 47 | (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals) 48 | }; 49 | 50 | if raw.is_null() { 51 | return None; 52 | } 53 | 54 | let transparency = Self::has_non_zero_alpha(display, raw); 55 | 56 | Some(Self { raw, transparency }) 57 | } 58 | 59 | #[cfg(glx_backend)] 60 | pub(crate) unsafe fn from_raw(display: *mut Display, raw: *const XVisualInfo) -> Self { 61 | let transparency = Self::has_non_zero_alpha(display, raw); 62 | Self { raw, transparency } 63 | } 64 | 65 | /// Returns `true` if the visual has non-zero alpha mask. 66 | pub fn supports_transparency(&self) -> bool { 67 | self.transparency 68 | } 69 | 70 | /// Get XID of for this visual. 71 | pub fn visual_id(&self) -> std::ffi::c_ulong { 72 | unsafe { (*self.raw).visualid } 73 | } 74 | 75 | /// Convert the visual to the raw pointer. 76 | /// 77 | /// You must clear it with `XFree` after the use. 78 | pub fn into_raw(self) -> *const std::ffi::c_void { 79 | let raw = self.raw as *const _; 80 | mem::forget(self); 81 | raw 82 | } 83 | 84 | pub(crate) fn has_non_zero_alpha(display: *mut Display, raw: *const XVisualInfo) -> bool { 85 | let xrender = XRENDER.as_ref().unwrap(); 86 | let visual_format = unsafe { (xrender.XRenderFindVisualFormat)(display, (*raw).visual) }; 87 | 88 | if !visual_format.is_null() { 89 | unsafe { (*visual_format).direct.alphaMask != 0 } 90 | } else { 91 | false 92 | } 93 | } 94 | } 95 | 96 | impl Drop for X11VisualInfo { 97 | fn drop(&mut self) { 98 | unsafe { 99 | (XLIB.as_ref().unwrap().XFree)(self.raw as *mut _); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /glutin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glutin" 3 | version = "0.32.3" 4 | authors = ["Kirill Chibisov "] 5 | description = "Cross-platform OpenGL context provider." 6 | keywords = ["windowing", "opengl", "egl"] 7 | license = "Apache-2.0" 8 | readme = "README.md" 9 | repository = "https://github.com/rust-windowing/glutin" 10 | documentation = "https://docs.rs/glutin" 11 | rust-version.workspace = true 12 | edition.workspace = true 13 | 14 | [features] 15 | default = ["egl", "glx", "x11", "wayland", "wgl"] 16 | egl = ["glutin_egl_sys", "libloading"] 17 | glx = ["x11", "glutin_glx_sys", "libloading"] 18 | wgl = ["glutin_wgl_sys", "windows-sys"] 19 | x11 = ["x11-dl"] 20 | wayland = ["wayland-sys", "egl"] 21 | 22 | [dependencies] 23 | bitflags = "2.2.1" 24 | libloading = { version = "0.8.0", optional = true } 25 | once_cell = "1.13" 26 | raw-window-handle = "0.6.2" 27 | 28 | [target.'cfg(windows)'.dependencies] 29 | glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys", optional = true } 30 | glutin_wgl_sys = { version = "0.6.1", path = "../glutin_wgl_sys", optional = true } 31 | 32 | [target.'cfg(windows)'.dependencies.windows-sys] 33 | version = "0.52" 34 | features = [ 35 | "Win32_Foundation", 36 | "Win32_Graphics_Gdi", 37 | "Win32_Graphics_OpenGL", 38 | "Win32_System_LibraryLoader", 39 | "Win32_UI_WindowsAndMessaging", 40 | ] 41 | optional = true 42 | 43 | [target.'cfg(target_os = "android")'.dependencies] 44 | glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys" } 45 | 46 | [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] 47 | glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys", optional = true } 48 | glutin_glx_sys = { version = "0.6.1", path = "../glutin_glx_sys", optional = true } 49 | wayland-sys = { version = "0.31.1", default-features = false, features = [ 50 | "egl", 51 | "client", 52 | "dlopen", 53 | ], optional = true } 54 | x11-dl = { version = "2.20.0", optional = true } 55 | 56 | [target.'cfg(any(target_os = "macos"))'.dependencies] 57 | dispatch2 = { version = "0.3.0", default-features = false, features = [ 58 | "std", 59 | "objc2", 60 | ] } 61 | objc2 = "0.6.1" 62 | objc2-core-foundation = { version = "0.3.2", default-features = false, features = [ 63 | "std", 64 | "CFBase", 65 | "CFString", 66 | "CFBundle", 67 | ] } 68 | objc2-foundation = { version = "0.3.2", default-features = false, features = [ 69 | "std", 70 | "NSArray", 71 | "NSThread", 72 | ] } 73 | objc2-app-kit = { version = "0.3.2", default-features = false, features = [ 74 | "std", 75 | "objc2-core-foundation", 76 | "objc2-open-gl", 77 | "NSApplication", 78 | "NSResponder", 79 | "NSView", 80 | "NSWindow", 81 | "NSOpenGL", 82 | "NSOpenGLView", 83 | ] } 84 | objc2-open-gl = { version = "0.3.2", default-features = false, features = [ 85 | "std", 86 | "CGLTypes", 87 | ] } 88 | 89 | [build-dependencies] 90 | cfg_aliases = "0.2.1" 91 | 92 | [package.metadata.docs.rs] 93 | rustdoc-args = ["--cfg", "docsrs"] 94 | targets = [ 95 | "aarch64-linux-android", 96 | "x86_64-unknown-linux-gnu", 97 | "x86_64-pc-windows-msvc", 98 | "x86_64-apple-darwin", 99 | "i686-unknown-linux-gnu", 100 | "i686-pc-windows-msvc", 101 | ] 102 | default-target = "x86_64-unknown-linux-gnu" 103 | -------------------------------------------------------------------------------- /glutin/src/api/cgl/display.rs: -------------------------------------------------------------------------------- 1 | //! A CGL display. 2 | 3 | use std::ffi::{self, CStr}; 4 | use std::marker::PhantomData; 5 | 6 | use objc2_core_foundation::{CFBundle, CFString}; 7 | use raw_window_handle::RawDisplayHandle; 8 | 9 | use crate::config::ConfigTemplate; 10 | use crate::display::{AsRawDisplay, DisplayFeatures, RawDisplay}; 11 | use crate::error::{ErrorKind, Result}; 12 | use crate::prelude::*; 13 | use crate::private::Sealed; 14 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; 15 | 16 | use super::config::Config; 17 | use super::context::NotCurrentContext; 18 | use super::surface::Surface; 19 | 20 | /// The CGL display. 21 | #[derive(Debug, Clone)] 22 | pub struct Display { 23 | // Prevent building of it without constructor. 24 | _marker: PhantomData<()>, 25 | } 26 | 27 | impl Display { 28 | /// Create CGL display. 29 | /// 30 | /// # Safety 31 | /// 32 | /// The function is unsafe for consistency. 33 | pub unsafe fn new(display: RawDisplayHandle) -> Result { 34 | match display { 35 | RawDisplayHandle::AppKit(..) => Ok(Display { _marker: PhantomData }), 36 | _ => Err(ErrorKind::NotSupported("provided native display is not supported").into()), 37 | } 38 | } 39 | } 40 | 41 | impl GlDisplay for Display { 42 | type Config = Config; 43 | type NotCurrentContext = NotCurrentContext; 44 | type PbufferSurface = Surface; 45 | type PixmapSurface = Surface; 46 | type WindowSurface = Surface; 47 | 48 | unsafe fn find_configs( 49 | &self, 50 | template: ConfigTemplate, 51 | ) -> Result + '_>> { 52 | unsafe { Self::find_configs(self, template) } 53 | } 54 | 55 | unsafe fn create_window_surface( 56 | &self, 57 | config: &Self::Config, 58 | surface_attributes: &SurfaceAttributes, 59 | ) -> Result { 60 | unsafe { Self::create_window_surface(self, config, surface_attributes) } 61 | } 62 | 63 | unsafe fn create_pbuffer_surface( 64 | &self, 65 | config: &Self::Config, 66 | surface_attributes: &SurfaceAttributes, 67 | ) -> Result { 68 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } 69 | } 70 | 71 | unsafe fn create_context( 72 | &self, 73 | config: &Self::Config, 74 | context_attributes: &crate::context::ContextAttributes, 75 | ) -> Result { 76 | unsafe { Self::create_context(self, config, context_attributes) } 77 | } 78 | 79 | unsafe fn create_pixmap_surface( 80 | &self, 81 | config: &Self::Config, 82 | surface_attributes: &SurfaceAttributes, 83 | ) -> Result { 84 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } 85 | } 86 | 87 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { 88 | let symbol_name = CFString::from_str(addr.to_str().unwrap()); 89 | let framework_name = CFString::from_static_str("com.apple.opengl"); 90 | let framework = CFBundle::bundle_with_identifier(Some(&framework_name)).unwrap(); 91 | CFBundle::function_pointer_for_name(&framework, Some(&symbol_name)) 92 | } 93 | 94 | fn version_string(&self) -> String { 95 | String::from("Apple CGL") 96 | } 97 | 98 | fn supported_features(&self) -> DisplayFeatures { 99 | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS 100 | | DisplayFeatures::FLOAT_PIXEL_FORMAT 101 | | DisplayFeatures::SRGB_FRAMEBUFFERS 102 | | DisplayFeatures::SWAP_CONTROL 103 | } 104 | } 105 | 106 | impl AsRawDisplay for Display { 107 | fn raw_display(&self) -> RawDisplay { 108 | RawDisplay::Cgl 109 | } 110 | } 111 | 112 | impl Sealed for Display {} 113 | -------------------------------------------------------------------------------- /glutin/src/api/egl/mod.rs: -------------------------------------------------------------------------------- 1 | //! EGL platform Api. 2 | //! 3 | //! This platform is typically available on Linux, Android and other Unix-like 4 | //! platforms. 5 | //! 6 | //! The EGL platform allows creating a [`Display`](self::display::Display) from 7 | //! a [`Device`](self::device::Device). 8 | 9 | use std::ffi::{self, CString}; 10 | use std::ops::{Deref, DerefMut}; 11 | 12 | use glutin_egl_sys::egl; 13 | 14 | use libloading::Library; 15 | use once_cell::sync::{Lazy, OnceCell}; 16 | 17 | #[cfg(unix)] 18 | use libloading::os::unix as libloading_os; 19 | #[cfg(windows)] 20 | use libloading::os::windows as libloading_os; 21 | 22 | use crate::error::{Error, ErrorKind, Result}; 23 | use crate::lib_loading::{SymLoading, SymWrapper}; 24 | 25 | pub mod config; 26 | pub mod context; 27 | pub mod device; 28 | pub mod display; 29 | pub mod surface; 30 | 31 | // WARNING: If this implementation is ever changed to unload or replace the 32 | // library, note that public API functions currently retirm `&'static str`ings 33 | // out of it, which would become invalid. 34 | pub(crate) static EGL: Lazy> = Lazy::new(|| { 35 | #[cfg(windows)] 36 | let paths = ["libEGL.dll", "atioglxx.dll"]; 37 | 38 | #[cfg(not(windows))] 39 | let paths = ["libEGL.so.1", "libEGL.so"]; 40 | 41 | unsafe { SymWrapper::new(&paths).map(Egl).ok() } 42 | }); 43 | 44 | type EglGetProcAddress = unsafe extern "system" fn(*const ffi::c_void) -> *const ffi::c_void; 45 | static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); 46 | 47 | /// EGL interface. 48 | #[allow(missing_debug_implementations)] 49 | pub struct Egl(SymWrapper); 50 | 51 | unsafe impl Sync for Egl {} 52 | unsafe impl Send for Egl {} 53 | 54 | impl SymLoading for egl::Egl { 55 | unsafe fn load_with(lib: &Library) -> Self { 56 | let loader = move |sym_name: &'static str| -> *const ffi::c_void { 57 | unsafe { 58 | let sym_name = CString::new(sym_name.as_bytes()).unwrap(); 59 | if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { 60 | return *sym; 61 | } 62 | 63 | let egl_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| { 64 | let sym: libloading::Symbol<'_, EglGetProcAddress> = 65 | lib.get(b"eglGetProcAddress\0").unwrap(); 66 | sym.into_raw() 67 | }); 68 | 69 | // The symbol was not available in the library, so ask eglGetProcAddress for it. 70 | // Note that eglGetProcAddress was only able to look up extension 71 | // functions prior to EGL 1.5, hence this two-part dance. 72 | (egl_proc_address)(sym_name.as_bytes_with_nul().as_ptr() as *const ffi::c_void) 73 | } 74 | }; 75 | 76 | egl::BindWaylandDisplayWL::load_with(loader); 77 | egl::UnbindWaylandDisplayWL::load_with(loader); 78 | egl::QueryWaylandBufferWL::load_with(loader); 79 | egl::CreateWaylandBufferFromImageWL::load_with(loader); 80 | 81 | Self::load_with(loader) 82 | } 83 | } 84 | 85 | impl Deref for Egl { 86 | type Target = egl::Egl; 87 | 88 | fn deref(&self) -> &Self::Target { 89 | &self.0 90 | } 91 | } 92 | 93 | impl DerefMut for Egl { 94 | fn deref_mut(&mut self) -> &mut Self::Target { 95 | &mut self.0 96 | } 97 | } 98 | 99 | /// Obtain the error from the EGL. 100 | fn check_error() -> Result<()> { 101 | let egl = EGL.as_ref().unwrap(); 102 | unsafe { 103 | let raw_code = egl.GetError() as egl::types::EGLenum; 104 | let kind = match raw_code { 105 | egl::SUCCESS => return Ok(()), 106 | egl::NOT_INITIALIZED => ErrorKind::InitializationFailed, 107 | egl::BAD_ACCESS => ErrorKind::BadAccess, 108 | egl::BAD_ALLOC => ErrorKind::OutOfMemory, 109 | egl::BAD_ATTRIBUTE => ErrorKind::BadAttribute, 110 | egl::BAD_CONTEXT => ErrorKind::BadContext, 111 | egl::BAD_CONFIG => ErrorKind::BadConfig, 112 | egl::BAD_CURRENT_SURFACE => ErrorKind::BadCurrentSurface, 113 | egl::BAD_DISPLAY => ErrorKind::BadDisplay, 114 | egl::BAD_SURFACE => ErrorKind::BadSurface, 115 | egl::BAD_MATCH => ErrorKind::BadMatch, 116 | egl::BAD_PARAMETER => ErrorKind::BadParameter, 117 | egl::BAD_NATIVE_PIXMAP => ErrorKind::BadNativePixmap, 118 | egl::BAD_NATIVE_WINDOW => ErrorKind::BadNativeWindow, 119 | egl::CONTEXT_LOST => ErrorKind::ContextLost, 120 | _ => ErrorKind::Misc, 121 | }; 122 | 123 | Err(Error::new(Some(raw_code as i64), None, kind)) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.rs' 7 | - '**.toml' 8 | - '.github/workflows/ci.yml' 9 | push: 10 | branches: [master] 11 | paths: 12 | - '**.rs' 13 | - '**.toml' 14 | - '.github/workflows/ci.yml' 15 | 16 | jobs: 17 | check-formatting: 18 | name: Check formatting 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: taiki-e/checkout-action@v1 22 | - uses: dtolnay/rust-toolchain@nightly 23 | with: 24 | components: rustfmt 25 | - uses: taiki-e/install-action@v2 26 | with: 27 | tool: typos-cli 28 | - name: Check Formatting 29 | run: cargo fmt --all -- --check 30 | - name: run typos 31 | run: typos 32 | - name: Typos info 33 | if: failure() 34 | run: | 35 | echo 'To fix typos, please run `typos -w`' 36 | echo 'To check for a diff, run `typos`' 37 | echo 'You can find typos here: https://crates.io/crates/typos' 38 | 39 | tests: 40 | name: Tests 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | rust_version: [1.85.0, stable, nightly] 45 | platform: 46 | - { target: x86_64-pc-windows-msvc, os: windows-latest, } 47 | - { target: i686-pc-windows-msvc, os: windows-latest, } 48 | - { target: i686-pc-windows-msvc, os: windows-latest, options: --no-default-features, features: wgl } 49 | - { target: i686-pc-windows-msvc, os: windows-latest, options: --no-default-features, features: egl } 50 | - { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu } 51 | - { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu } 52 | - { target: i686-unknown-linux-gnu, os: ubuntu-latest, } 53 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, } 54 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "egl,wayland,x11" } 55 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "egl,wayland" } 56 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "egl,x11" } 57 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: glx } 58 | - { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' } 59 | - { target: x86_64-apple-darwin, os: macos-latest, } 60 | exclude: 61 | # Android is tested on stable-3 62 | - rust_version: '1.85.0' 63 | platform: { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' } 64 | include: 65 | - rust_version: '1.85.0' 66 | platform: { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' } 67 | 68 | 69 | env: 70 | RUST_BACKTRACE: 1 71 | CARGO_INCREMENTAL: 0 72 | RUSTFLAGS: "-C debuginfo=0" 73 | OPTIONS: ${{ matrix.platform.options }} 74 | CMD: ${{ matrix.platform.cmd }} 75 | FEATURES: ${{ format(',{0}', matrix.platform.features ) }} 76 | RUSTDOCFLAGS: -Dwarnings 77 | 78 | runs-on: ${{ matrix.platform.os }} 79 | steps: 80 | - uses: actions/checkout@v3 81 | - uses: hecrj/setup-rust-action@v1 82 | with: 83 | rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }} 84 | targets: ${{ matrix.platform.target }} 85 | components: clippy 86 | 87 | # We need those for examples. 88 | - name: Install GCC Multilib 89 | if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') 90 | run: sudo apt-get update && sudo apt-get install gcc-multilib 91 | 92 | - name: Install cargo-apk 93 | if: contains(matrix.platform.target, 'android') 94 | run: cargo +stable install cargo-apk 95 | 96 | - name: Build tests 97 | shell: bash 98 | run: cargo $CMD test -p glutin --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES 99 | - name: Run tests 100 | shell: bash 101 | if: ( 102 | !contains(matrix.platform.target, 'android') && 103 | !contains(matrix.platform.target, 'ios') && 104 | !contains(matrix.platform.target, 'wasm32')) 105 | run: cargo test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES 106 | 107 | - name: Check documentation 108 | shell: bash 109 | run: cd glutin && cargo doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items 110 | 111 | - name: Lint with clippy 112 | shell: bash 113 | if: (matrix.rust_version == '1.85.0') && !contains(matrix.platform.options, '--no-default-features') 114 | run: cargo clippy --workspace --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings 115 | -------------------------------------------------------------------------------- /glutin/src/api/wgl/mod.rs: -------------------------------------------------------------------------------- 1 | //! WGL Api. 2 | 3 | use std::collections::HashSet; 4 | use std::ffi::{CString, OsStr}; 5 | use std::io::Error as IoError; 6 | use std::mem; 7 | use std::ops::Deref; 8 | use std::os::windows::ffi::OsStrExt; 9 | 10 | use glutin_wgl_sys::{wgl, wgl_extra}; 11 | use once_cell::sync::OnceCell; 12 | use windows_sys::Win32::Foundation::{HMODULE, HWND}; 13 | use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; 14 | use windows_sys::Win32::UI::WindowsAndMessaging::{self as wm, WINDOWPLACEMENT, WNDCLASSEXW}; 15 | 16 | use crate::error::{Error, ErrorKind, Result}; 17 | 18 | pub mod config; 19 | pub mod context; 20 | pub mod display; 21 | pub mod surface; 22 | 23 | pub(crate) static WGL_EXTRA: OnceCell = OnceCell::new(); 24 | 25 | pub(crate) struct WglExtra(wgl_extra::Wgl); 26 | 27 | unsafe impl Send for WglExtra {} 28 | unsafe impl Sync for WglExtra {} 29 | 30 | impl WglExtra { 31 | fn new() -> Self { 32 | Self(wgl_extra::Wgl::load_with(|addr| unsafe { 33 | let addr = CString::new(addr.as_bytes()).unwrap(); 34 | let addr = addr.as_ptr(); 35 | wgl::GetProcAddress(addr).cast() 36 | })) 37 | } 38 | } 39 | 40 | impl Deref for WglExtra { 41 | type Target = wgl_extra::Wgl; 42 | 43 | fn deref(&self) -> &Self::Target { 44 | &self.0 45 | } 46 | } 47 | 48 | unsafe fn load_extra_functions( 49 | instance: HMODULE, 50 | win: HWND, 51 | ) -> Result<(&'static WglExtra, HashSet<&'static str>)> { 52 | let rect = unsafe { 53 | let mut placement: WINDOWPLACEMENT = std::mem::zeroed(); 54 | placement.length = mem::size_of::() as _; 55 | if wm::GetWindowPlacement(win, &mut placement) == 0 { 56 | return Err(IoError::last_os_error().into()); 57 | } 58 | placement.rcNormalPosition 59 | }; 60 | 61 | let mut class_name = [0u16; 128]; 62 | unsafe { 63 | if wm::GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { 64 | return Err(IoError::last_os_error().into()); 65 | } 66 | } 67 | 68 | let mut class = unsafe { 69 | let mut class: WNDCLASSEXW = std::mem::zeroed(); 70 | if wm::GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 { 71 | return Err(IoError::last_os_error().into()); 72 | } 73 | 74 | class 75 | }; 76 | 77 | let class_name = OsStr::new("WglDummy Window").encode_wide().chain(Some(0)).collect::>(); 78 | 79 | class.cbSize = mem::size_of::() as _; 80 | class.lpszClassName = class_name.as_ptr(); 81 | class.lpfnWndProc = Some(wm::DefWindowProcW); 82 | 83 | // This shouldn't fail if the registration of the real window class 84 | // worked. Multiple registrations of the window class trigger an 85 | // error which we want to ignore silently (e.g for multi-window 86 | // setups). 87 | unsafe { wm::RegisterClassExW(&class) }; 88 | 89 | // This dummy window should match the real one enough to get the same OpenGL 90 | // driver. 91 | let title = OsStr::new("dummy window").encode_wide().chain(Some(0)).collect::>(); 92 | 93 | let ex_style = wm::WS_EX_APPWINDOW; 94 | let style = wm::WS_POPUP | wm::WS_CLIPSIBLINGS | wm::WS_CLIPCHILDREN; 95 | let win = unsafe { 96 | wm::CreateWindowExW( 97 | ex_style, 98 | class_name.as_ptr(), 99 | title.as_ptr() as _, 100 | style, 101 | wm::CW_USEDEFAULT, 102 | wm::CW_USEDEFAULT, 103 | rect.right - rect.left, 104 | rect.bottom - rect.top, 105 | 0, 106 | 0, 107 | instance, 108 | std::ptr::null_mut(), 109 | ) 110 | }; 111 | 112 | if win == 0 { 113 | return Err(IoError::last_os_error().into()); 114 | } 115 | 116 | let hdc = unsafe { gdi::GetDC(win) }; 117 | unsafe { 118 | let (pixel_format_index, descriptor) = config::choose_dummy_pixel_format(hdc)?; 119 | if gl::SetPixelFormat(hdc, pixel_format_index, &descriptor) == 0 { 120 | return Err(IoError::last_os_error().into()); 121 | } 122 | }; 123 | 124 | let context = unsafe { 125 | let context = gl::wglCreateContext(hdc); 126 | if gl::wglMakeCurrent(hdc, context) == 0 { 127 | return Err(IoError::last_os_error().into()); 128 | } 129 | context 130 | }; 131 | 132 | // Load WGL. 133 | let wgl_extra = WGL_EXTRA.get_or_init(WglExtra::new); 134 | let client_extensions = display::load_extensions(hdc, wgl_extra); 135 | 136 | unsafe { 137 | wm::DestroyWindow(win); 138 | gl::wglDeleteContext(context); 139 | } 140 | 141 | Ok((wgl_extra, client_extensions)) 142 | } 143 | 144 | impl From for Error { 145 | fn from(error: std::io::Error) -> Self { 146 | let raw = error.raw_os_error().map(|code| code as i64); 147 | Error::new(raw, Some(error.to_string()), ErrorKind::Misc) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /glutin_examples/examples/egl_device.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(egl_backend)] 3 | example::run(); 4 | } 5 | 6 | #[cfg(egl_backend)] 7 | mod example { 8 | use std::fs::OpenOptions; 9 | use std::path::Path; 10 | 11 | use glutin::api::egl::device::Device; 12 | use glutin::api::egl::display::Display; 13 | use glutin::config::{ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder}; 14 | use glutin::context::{ContextApi, ContextAttributesBuilder}; 15 | use glutin::prelude::*; 16 | use glutin_examples::{Renderer, gl}; 17 | 18 | const IMG_PATH: &str = concat!(env!("OUT_DIR"), "/egl_device.png"); 19 | 20 | pub fn run() { 21 | let devices = Device::query_devices().expect("Failed to query devices").collect::>(); 22 | 23 | for (index, device) in devices.iter().enumerate() { 24 | println!( 25 | "Device {}: Name: {} Vendor: {}", 26 | index, 27 | device.name().unwrap_or("UNKNOWN"), 28 | device.vendor().unwrap_or("UNKNOWN") 29 | ); 30 | } 31 | 32 | let device = devices.first().expect("No available devices"); 33 | 34 | // Create a display using the device. 35 | let display = 36 | unsafe { Display::with_device(device, None) }.expect("Failed to create display"); 37 | 38 | let template = config_template(); 39 | let config = unsafe { display.find_configs(template) } 40 | .unwrap() 41 | .reduce( 42 | |config, acc| { 43 | if config.num_samples() > acc.num_samples() { config } else { acc } 44 | }, 45 | ) 46 | .expect("No available configs"); 47 | 48 | println!("Picked a config with {} samples", config.num_samples()); 49 | 50 | // Context creation. 51 | // 52 | // In particular, since we are doing offscreen rendering we have no raw window 53 | // handle to provide. 54 | let context_attributes = ContextAttributesBuilder::new().build(None); 55 | 56 | // Since glutin by default tries to create OpenGL core context, which may not be 57 | // present we should try gles. 58 | let fallback_context_attributes = 59 | ContextAttributesBuilder::new().with_context_api(ContextApi::Gles(None)).build(None); 60 | 61 | let not_current = unsafe { 62 | display.create_context(&config, &context_attributes).unwrap_or_else(|_| { 63 | display 64 | .create_context(&config, &fallback_context_attributes) 65 | .expect("failed to create context") 66 | }) 67 | }; 68 | 69 | // Make the context current for rendering 70 | let _context = not_current.make_current_surfaceless().unwrap(); 71 | let renderer = Renderer::new(&display); 72 | 73 | // Create a framebuffer for offscreen rendering since we do not have a window. 74 | let mut framebuffer = 0; 75 | let mut renderbuffer = 0; 76 | unsafe { 77 | renderer.GenFramebuffers(1, &mut framebuffer); 78 | renderer.GenRenderbuffers(1, &mut renderbuffer); 79 | renderer.BindFramebuffer(gl::FRAMEBUFFER, framebuffer); 80 | renderer.BindRenderbuffer(gl::RENDERBUFFER, renderbuffer); 81 | renderer.RenderbufferStorage(gl::RENDERBUFFER, gl::RGBA, 1280, 720); 82 | renderer.FramebufferRenderbuffer( 83 | gl::FRAMEBUFFER, 84 | gl::COLOR_ATTACHMENT0, 85 | gl::RENDERBUFFER, 86 | renderbuffer, 87 | ); 88 | } 89 | 90 | renderer.resize(1280, 720); 91 | renderer.draw(); 92 | 93 | let mut buffer = Vec::::with_capacity(1280 * 720 * 4); 94 | unsafe { 95 | // Wait for the previous commands to finish before reading from the framebuffer. 96 | renderer.Finish(); 97 | // Download the framebuffer contents to the buffer. 98 | renderer.ReadPixels( 99 | 0, 100 | 0, 101 | 1280, 102 | 720, 103 | gl::RGBA, 104 | gl::UNSIGNED_BYTE, 105 | buffer.as_mut_ptr() as *mut _, 106 | ); 107 | buffer.set_len(1280 * 720 * 4); 108 | } 109 | 110 | let path = Path::new(IMG_PATH); 111 | let file = OpenOptions::new().create(true).write(true).truncate(true).open(path).unwrap(); 112 | 113 | let mut encoder = png::Encoder::new(file, 1280, 720); 114 | encoder.set_depth(png::BitDepth::Eight); 115 | encoder.set_color(png::ColorType::Rgba); 116 | let mut png_writer = encoder.write_header().unwrap(); 117 | 118 | png_writer.write_image_data(&buffer[..]).unwrap(); 119 | png_writer.finish().unwrap(); 120 | println!("Output rendered to: {}", path.display()); 121 | 122 | unsafe { 123 | // Unbind the framebuffer and renderbuffer before deleting. 124 | renderer.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); 125 | renderer.BindRenderbuffer(gl::RENDERBUFFER, 0); 126 | renderer.DeleteFramebuffers(1, &framebuffer); 127 | renderer.DeleteRenderbuffers(1, &renderbuffer); 128 | } 129 | } 130 | 131 | fn config_template() -> ConfigTemplate { 132 | ConfigTemplateBuilder::default() 133 | .with_alpha_size(8) 134 | // Offscreen rendering has no support window surface support. 135 | .with_surface_type(ConfigSurfaceTypes::empty()) 136 | .build() 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /glutin/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Glutin error handling. 2 | 3 | use std::fmt; 4 | 5 | /// A specialized [`Result`] type for graphics operations. 6 | pub type Result = std::result::Result; 7 | 8 | /// The error type for all the graphics platform operations. 9 | #[derive(Debug, Clone)] 10 | pub struct Error { 11 | /// The raw code of the underlying error. 12 | raw_code: Option, 13 | 14 | /// The raw message from the os in case it could be obtained. 15 | raw_os_message: Option, 16 | 17 | /// The simplified error kind to handle matching. 18 | kind: ErrorKind, 19 | } 20 | 21 | impl Error { 22 | #[allow(dead_code)] 23 | pub(crate) fn new( 24 | raw_code: Option, 25 | raw_os_message: Option, 26 | kind: ErrorKind, 27 | ) -> Self { 28 | Self { raw_code, raw_os_message, kind } 29 | } 30 | 31 | /// Helper to check that error is [`ErrorKind::NotSupported`]. 32 | #[inline] 33 | pub fn not_supported(&self) -> bool { 34 | matches!(&self.kind, ErrorKind::NotSupported(_)) 35 | } 36 | 37 | /// The underlying error kind. 38 | #[inline] 39 | pub fn error_kind(&self) -> ErrorKind { 40 | self.kind 41 | } 42 | 43 | /// The underlying raw code in case it's present. 44 | #[inline] 45 | pub fn raw_code(&self) -> Option { 46 | self.raw_code 47 | } 48 | } 49 | 50 | impl fmt::Display for Error { 51 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 | if let Some(raw_code) = self.raw_code { 53 | write!(f, "[{raw_code:x}] ")?; 54 | } 55 | 56 | let msg = if let Some(raw_os_message) = self.raw_os_message.as_ref() { 57 | raw_os_message 58 | } else { 59 | self.kind.as_str() 60 | }; 61 | 62 | write!(f, "{msg}") 63 | } 64 | } 65 | 66 | impl std::error::Error for Error {} 67 | 68 | /// Build an error with just a kind. 69 | impl From for Error { 70 | fn from(kind: ErrorKind) -> Self { 71 | Error { raw_code: None, raw_os_message: None, kind } 72 | } 73 | } 74 | 75 | /// A list specifying general categoires of native platform graphics interface 76 | /// errors. 77 | #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] 78 | pub enum ErrorKind { 79 | /// The requested display wasn't found or some required symbol in it was 80 | /// missing. 81 | NotFound, 82 | 83 | /// Failed to perform resource initialization. 84 | InitializationFailed, 85 | 86 | /// Can't access a requested resource. 87 | /// 88 | /// For example when trying to make a context current while it's current on 89 | /// another thread. 90 | BadAccess, 91 | 92 | /// An operation could not be completed, because it failed to allocate 93 | /// enough memory. 94 | OutOfMemory, 95 | 96 | /// An recognized attribute value was passed. 97 | BadAttribute, 98 | 99 | /// The context is no longer valid. 100 | BadContext, 101 | 102 | /// The context is in bad state. 103 | BadContextState, 104 | 105 | /// Invalid config was passed. 106 | BadConfig, 107 | 108 | /// The current surface of the calling thread is no longer valid. 109 | BadCurrentSurface, 110 | 111 | /// The display is no longer valid. 112 | BadDisplay, 113 | 114 | /// The surface is invalid. 115 | BadSurface, 116 | 117 | /// The pbuffer is invalid. 118 | BadPbuffer, 119 | 120 | /// The pixmap is invalid. 121 | BadPixmap, 122 | 123 | /// Arguments are inconsistent. For example when shared contexts are not 124 | /// compatible. 125 | BadMatch, 126 | 127 | /// One or more argument values are invalid. 128 | BadParameter, 129 | 130 | /// Bad native pixmap was provided. 131 | BadNativePixmap, 132 | 133 | /// Bad native window was provided. 134 | BadNativeWindow, 135 | 136 | /// The context was lost. 137 | ContextLost, 138 | 139 | /// The operation is not supported by the platform. 140 | NotSupported(&'static str), 141 | 142 | /// The misc error that can't be classified occurred. 143 | Misc, 144 | } 145 | 146 | impl ErrorKind { 147 | pub(crate) fn as_str(&self) -> &'static str { 148 | use ErrorKind::*; 149 | match *self { 150 | NotFound => "not found", 151 | InitializationFailed => "initialization failed", 152 | BadAccess => "access to the resource failed", 153 | OutOfMemory => "out of memory", 154 | BadAttribute => "an unrecognized attribute or attribute value was passed", 155 | BadContext => "argument does not name a valid context", 156 | BadContextState => "the context is in a bad state", 157 | BadConfig => "argument does not name a valid config", 158 | BadCurrentSurface => "the current surface of the calling thread is no longer valid", 159 | BadDisplay => "argument does not name a valid display", 160 | BadSurface => "argument does not name a valid surface", 161 | BadPbuffer => "argument does not name a valid pbuffer", 162 | BadPixmap => "argument does not name a valid pixmap", 163 | BadMatch => "arguments are inconsistent", 164 | BadParameter => "one or more argument values are invalid", 165 | BadNativePixmap => "argument does not refer to a valid native pixmap", 166 | BadNativeWindow => "argument does not refer to a valid native window", 167 | ContextLost => "context loss", 168 | NotSupported(reason) => reason, 169 | Misc => "misc platform error", 170 | } 171 | } 172 | } 173 | 174 | impl fmt::Display for ErrorKind { 175 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 176 | f.write_str(self.as_str()) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /glutin/src/api/cgl/surface.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper around `NSView`. 2 | 3 | use std::fmt; 4 | use std::marker::PhantomData; 5 | use std::num::NonZeroU32; 6 | 7 | use dispatch2::{MainThreadBound, run_on_main}; 8 | use objc2::MainThreadMarker; 9 | use objc2::rc::Retained; 10 | use objc2_app_kit::{NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSView}; 11 | use raw_window_handle::RawWindowHandle; 12 | 13 | use crate::config::GetGlConfig; 14 | use crate::display::GetGlDisplay; 15 | use crate::error::{ErrorKind, Result}; 16 | use crate::private::Sealed; 17 | use crate::surface::{ 18 | AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, 19 | SurfaceTypeTrait, SwapInterval, WindowSurface, 20 | }; 21 | 22 | use super::config::Config; 23 | use super::context::PossiblyCurrentContext; 24 | use super::display::Display; 25 | 26 | impl Display { 27 | pub(crate) unsafe fn create_pixmap_surface( 28 | &self, 29 | _config: &Config, 30 | _surface_attributes: &SurfaceAttributes, 31 | ) -> Result> { 32 | Err(ErrorKind::NotSupported("pixmaps are not supported with CGL").into()) 33 | } 34 | 35 | pub(crate) unsafe fn create_pbuffer_surface( 36 | &self, 37 | _config: &Config, 38 | _surface_attributes: &SurfaceAttributes, 39 | ) -> Result> { 40 | Err(ErrorKind::NotSupported("pbuffers are not supported with CGL").into()) 41 | } 42 | 43 | pub(crate) unsafe fn create_window_surface( 44 | &self, 45 | config: &Config, 46 | surface_attributes: &SurfaceAttributes, 47 | ) -> Result> { 48 | let native_window = match surface_attributes.raw_window_handle.unwrap() { 49 | RawWindowHandle::AppKit(window) => window, 50 | _ => { 51 | return Err( 52 | ErrorKind::NotSupported("provided native window is not supported").into() 53 | ); 54 | }, 55 | }; 56 | 57 | // SAFETY: The objects below must have been created on the main thread 58 | // in the first place, so we can safely "move" them back to that thread. 59 | let mtm = unsafe { MainThreadMarker::new_unchecked() }; 60 | 61 | // SAFETY: Validity of the view and window is ensured by caller 62 | // This function makes sure the window is non null. 63 | let ns_view = if let Some(ns_view) = 64 | unsafe { Retained::retain(native_window.ns_view.as_ptr().cast::()) } 65 | { 66 | ns_view 67 | } else { 68 | return Err(ErrorKind::NotSupported("ns_view of provided native window is nil").into()); 69 | }; 70 | 71 | // The default value of `wantsBestResolutionOpenGLSurface` is `false` when 72 | // linked with the macOS 10.14 SDK and `true` if linked with a macOS 10.15 SDK 73 | // or newer. We always set it to `true` because we want High DPI surfaces, and 74 | // we want to avoid this confusing default system value. 75 | #[allow(deprecated)] 76 | ns_view.setWantsBestResolutionOpenGLSurface(true); 77 | 78 | // On Mojave, views apparently automatically become layer-backed shortly after 79 | // being added to a window. Changing the layer-backedness of a view breaks the 80 | // association between the view and its associated OpenGL context. To work 81 | // around this, we explicitly make the view layer-backed up front so that AppKit 82 | // doesn't do it itself and break the association with its context. 83 | if unsafe { NSAppKitVersionNumber }.floor() > NSAppKitVersionNumber10_12 { 84 | ns_view.setWantsLayer(true); 85 | } 86 | 87 | let ns_view = MainThreadBound::new(ns_view, mtm); 88 | 89 | let surface = Surface { 90 | display: self.clone(), 91 | config: config.clone(), 92 | ns_view, 93 | _nosync: PhantomData, 94 | _ty: PhantomData, 95 | }; 96 | Ok(surface) 97 | } 98 | } 99 | 100 | /// A wrapper around `NSView`. 101 | pub struct Surface { 102 | display: Display, 103 | config: Config, 104 | pub(crate) ns_view: MainThreadBound>, 105 | _nosync: PhantomData<*const std::ffi::c_void>, 106 | _ty: PhantomData, 107 | } 108 | 109 | // Impl only `Send` for Surface. 110 | unsafe impl Send for Surface {} 111 | 112 | impl GlSurface for Surface { 113 | type Context = PossiblyCurrentContext; 114 | type SurfaceType = T; 115 | 116 | fn buffer_age(&self) -> u32 { 117 | 0 118 | } 119 | 120 | fn width(&self) -> Option { 121 | let view = &self.ns_view; 122 | run_on_main(|mtm| { 123 | let view = view.get(mtm); 124 | let scale_factor = match view.window() { 125 | Some(window) => window.backingScaleFactor(), 126 | None => 1.0, 127 | }; 128 | let frame = view.frame(); 129 | Some((frame.size.width * scale_factor) as u32) 130 | }) 131 | } 132 | 133 | fn height(&self) -> Option { 134 | let view = &self.ns_view; 135 | run_on_main(|mtm| { 136 | let view = view.get(mtm); 137 | let scale_factor = match view.window() { 138 | Some(window) => window.backingScaleFactor(), 139 | None => 1.0, 140 | }; 141 | let frame = view.frame(); 142 | Some((frame.size.height * scale_factor) as u32) 143 | }) 144 | } 145 | 146 | fn is_single_buffered(&self) -> bool { 147 | self.config.is_single_buffered() 148 | } 149 | 150 | fn swap_buffers(&self, context: &Self::Context) -> Result<()> { 151 | context.inner.flush_buffer() 152 | } 153 | 154 | fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> { 155 | context.inner.set_swap_interval(interval); 156 | Ok(()) 157 | } 158 | 159 | fn is_current(&self, context: &Self::Context) -> bool { 160 | context.inner.is_view_current(&self.ns_view) 161 | } 162 | 163 | fn is_current_draw(&self, context: &Self::Context) -> bool { 164 | self.is_current(context) 165 | } 166 | 167 | fn is_current_read(&self, context: &Self::Context) -> bool { 168 | self.is_current(context) 169 | } 170 | 171 | fn resize(&self, context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { 172 | context.inner.update(); 173 | } 174 | } 175 | 176 | impl GetGlConfig for Surface { 177 | type Target = Config; 178 | 179 | fn config(&self) -> Self::Target { 180 | self.config.clone() 181 | } 182 | } 183 | 184 | impl GetGlDisplay for Surface { 185 | type Target = Display; 186 | 187 | fn display(&self) -> Self::Target { 188 | self.display.clone() 189 | } 190 | } 191 | 192 | impl AsRawSurface for Surface { 193 | fn raw_surface(&self) -> RawSurface { 194 | // SAFETY: We only use the thread marker to get the pointer value of the view 195 | let mtm = unsafe { MainThreadMarker::new_unchecked() }; 196 | RawSurface::Cgl(Retained::as_ptr(self.ns_view.get(mtm)).cast()) 197 | } 198 | } 199 | 200 | impl fmt::Debug for Surface { 201 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 202 | f.debug_struct("Surface") 203 | .field("config", &self.config.inner.raw) 204 | .field("ns_view", &self.ns_view) 205 | .field("type", &T::surface_type()) 206 | .finish() 207 | } 208 | } 209 | 210 | impl Sealed for Surface {} 211 | -------------------------------------------------------------------------------- /glutin/src/api/glx/mod.rs: -------------------------------------------------------------------------------- 1 | //! GLX platform Api. 2 | #![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support 3 | 4 | use std::ffi::{self, CStr, CString}; 5 | use std::ops::{Deref, DerefMut}; 6 | use std::sync::Mutex; 7 | use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; 8 | 9 | use libloading::Library; 10 | use once_cell::sync::Lazy; 11 | use x11_dl::xlib::{self, XErrorEvent}; 12 | 13 | use glutin_glx_sys::{glx, glx_extra}; 14 | 15 | use crate::error::{Error, ErrorKind, Result}; 16 | use crate::lib_loading::{SymLoading, SymWrapper}; 17 | use crate::platform::x11::XLIB; 18 | 19 | pub mod config; 20 | pub mod context; 21 | pub mod display; 22 | pub mod surface; 23 | 24 | /// When using Xlib we need to get errors from it somehow, however creating 25 | /// inner `XDisplay` to handle that or change the error hook is unsafe in 26 | /// multithreaded applications, given that error hook is per process and not 27 | /// connection. 28 | /// 29 | /// The hook registrar must call to the function inside xlib error 30 | /// [`handler`]. 31 | /// 32 | /// The `bool` value returned by that hook tells whether the error was handled 33 | /// by it or not. So when it returns `true` it means that your error handling 34 | /// routine shouldn't handle the error as it was handled by the hook already. 35 | /// 36 | /// [`handler`]: https://tronche.com/gui/x/xlib/event-handling/protocol-errors/XSetErrorHandler.html 37 | pub type XlibErrorHookRegistrar = 38 | Box bool + Send + Sync>)>; 39 | 40 | /// The base used for GLX errors. 41 | static GLX_BASE_ERROR: AtomicI32 = AtomicI32::new(0); 42 | 43 | /// The last error arrived from GLX normalized by `GLX_BASE_ERROR`. 44 | static LAST_GLX_ERROR: Lazy>> = Lazy::new(|| Mutex::new(None)); 45 | 46 | /// Whether we're in the process of getting GLX error. Otherwise we may handle 47 | /// the winit's error. 48 | static SYNCING_GLX_ERROR: AtomicBool = AtomicBool::new(false); 49 | 50 | static GLX: Lazy> = Lazy::new(|| { 51 | let paths = ["libGL.so.1", "libGL.so"]; 52 | 53 | unsafe { SymWrapper::new(&paths).map(Glx).ok() } 54 | }); 55 | 56 | static GLX_EXTRA: Lazy> = Lazy::new(|| { 57 | let glx = GLX.as_ref()?; 58 | Some(GlxExtra::new(glx)) 59 | }); 60 | 61 | /// GLX interface. 62 | #[allow(missing_debug_implementations)] 63 | pub struct Glx(pub SymWrapper); 64 | 65 | unsafe impl Sync for Glx {} 66 | unsafe impl Send for Glx {} 67 | 68 | impl SymLoading for glx::Glx { 69 | unsafe fn load_with(lib: &Library) -> Self { 70 | Self::load_with(|sym| unsafe { 71 | lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) 72 | .map(|sym| *sym) 73 | .unwrap_or(std::ptr::null_mut()) 74 | }) 75 | } 76 | } 77 | 78 | impl Deref for Glx { 79 | type Target = glx::Glx; 80 | 81 | fn deref(&self) -> &Self::Target { 82 | &self.0 83 | } 84 | } 85 | 86 | impl DerefMut for Glx { 87 | #[inline] 88 | fn deref_mut(&mut self) -> &mut Self::Target { 89 | &mut self.0 90 | } 91 | } 92 | 93 | pub(crate) struct GlxExtra(glx_extra::Glx); 94 | 95 | unsafe impl Sync for GlxExtra {} 96 | unsafe impl Send for GlxExtra {} 97 | 98 | impl GlxExtra { 99 | #[inline] 100 | pub fn new(glx: &Glx) -> Self { 101 | GlxExtra(glx_extra::Glx::load_with(|proc_name| { 102 | let c_str = CString::new(proc_name).unwrap(); 103 | unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } 104 | })) 105 | } 106 | } 107 | 108 | impl Deref for GlxExtra { 109 | type Target = glx_extra::Glx; 110 | 111 | fn deref(&self) -> &Self::Target { 112 | &self.0 113 | } 114 | } 115 | 116 | impl DerefMut for GlxExtra { 117 | #[inline] 118 | fn deref_mut(&mut self) -> &mut Self::Target { 119 | &mut self.0 120 | } 121 | } 122 | /// Store the last error received from the GLX. 123 | fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool { 124 | // In case we've not forced the sync, ignore the error. 125 | if !SYNCING_GLX_ERROR.load(Ordering::Relaxed) { 126 | return false; 127 | } 128 | 129 | let xerror = xerror_event as *mut XErrorEvent; 130 | unsafe { 131 | let code = (*xerror).error_code; 132 | let glx_code = code as i32 - GLX_BASE_ERROR.load(Ordering::Relaxed); 133 | 134 | // Get the kind of the error. 135 | let kind = match code as u8 { 136 | xlib::BadValue => ErrorKind::BadAttribute, 137 | xlib::BadMatch => ErrorKind::BadMatch, 138 | xlib::BadWindow => ErrorKind::BadNativeWindow, 139 | xlib::BadAlloc => ErrorKind::OutOfMemory, 140 | xlib::BadPixmap => ErrorKind::BadPixmap, 141 | xlib::BadAccess => ErrorKind::BadAccess, 142 | _ if glx_code >= 0 => match glx_code as glx::types::GLenum { 143 | glx::PROTO_BAD_CONTEXT => ErrorKind::BadContext, 144 | glx::PROTO_BAD_CONTEXT_STATE => ErrorKind::BadContext, 145 | glx::PROTO_BAD_CURRENT_DRAWABLE => ErrorKind::BadCurrentSurface, 146 | glx::PROTO_BAD_CURRENT_WINDOW => ErrorKind::BadCurrentSurface, 147 | glx::PROTO_BAD_FBCONFIG => ErrorKind::BadConfig, 148 | glx::PROTO_BAD_PBUFFER => ErrorKind::BadPbuffer, 149 | glx::PROTO_BAD_PIXMAP => ErrorKind::BadPixmap, 150 | glx::PROTO_UNSUPPORTED_PRIVATE_REQUEST => ErrorKind::Misc, 151 | glx::PROTO_BAD_DRAWABLE => ErrorKind::BadSurface, 152 | glx::PROTO_BAD_WINDOW => ErrorKind::BadSurface, 153 | glx::PROTO_BAD_CONTEXT_TAG => ErrorKind::Misc, 154 | glx::PROTO_BAD_RENDER_REQUEST => ErrorKind::Misc, 155 | glx::PROTO_BAD_LARGE_REQUEST => ErrorKind::Misc, 156 | _ => return false, 157 | }, 158 | _ => return false, 159 | }; 160 | 161 | // Get the string from X11 error. 162 | let mut buf = vec![0u8; 1024]; 163 | (XLIB.as_ref().unwrap().XGetErrorText)( 164 | _display as *mut _, 165 | (*xerror).error_code as _, 166 | buf.as_mut_ptr() as *mut _, 167 | buf.len() as _, 168 | ); 169 | let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string(); 170 | 171 | *LAST_GLX_ERROR.lock().unwrap() = 172 | Some(Error::new(Some(code as _), Some(description), kind)); 173 | 174 | true 175 | } 176 | } 177 | 178 | /// Prevent error being overwritten when accessing the handler from the multiple 179 | /// threads. 180 | static ERROR_SECTION_LOCK: Mutex<()> = Mutex::new(()); 181 | 182 | /// Get the error from the X11. 183 | /// 184 | /// XXX mesa and I'd guess other GLX implementations, send the error, by taking 185 | /// the Xlib Error handling hook, getting the current hook, and calling back to 186 | /// the user, meaning that no `XSync` should be done. 187 | fn last_glx_error T>(callback: F) -> Result { 188 | let _guard = ERROR_SECTION_LOCK.lock().unwrap(); 189 | 190 | // Mark that we're syncing the error. 191 | SYNCING_GLX_ERROR.store(true, Ordering::Relaxed); 192 | 193 | // Execute the user routine that may produce GLX error. 194 | let result = callback(); 195 | 196 | // XXX We might want to XSync here in addition, because what mesa is doing might 197 | // not be common, but I'd assume that what mesa doing is common. 198 | 199 | // Reset and report last error. 200 | let result = match LAST_GLX_ERROR.lock().unwrap().take() { 201 | Some(error) => Err(error), 202 | None => Ok(result), 203 | }; 204 | 205 | // Release the mark. 206 | SYNCING_GLX_ERROR.store(false, Ordering::Relaxed); 207 | 208 | result 209 | } 210 | -------------------------------------------------------------------------------- /glutin/src/api/cgl/config.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to `NSOpenGLPixelFormat`. 2 | 3 | use std::ptr::NonNull; 4 | use std::sync::Arc; 5 | use std::{fmt, iter}; 6 | 7 | use objc2::AllocAnyThread; 8 | use objc2::rc::Retained; 9 | #[allow(deprecated)] 10 | use objc2_app_kit::{ 11 | NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, NSOpenGLPFAAlphaSize, 12 | NSOpenGLPFAColorFloat, NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, 13 | NSOpenGLPFAMinimumPolicy, NSOpenGLPFAMultisample, NSOpenGLPFAOpenGLProfile, 14 | NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize, NSOpenGLPFAStereo, 15 | NSOpenGLPFATripleBuffer, NSOpenGLPixelFormat, NSOpenGLPixelFormatAttribute, 16 | NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersionLegacy, 17 | }; 18 | 19 | use crate::config::{ 20 | Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, 21 | }; 22 | use crate::display::GetGlDisplay; 23 | use crate::error::{ErrorKind, Result}; 24 | use crate::private::Sealed; 25 | 26 | use super::display::Display; 27 | 28 | impl Display { 29 | #[allow(deprecated)] 30 | pub(crate) unsafe fn find_configs( 31 | &self, 32 | template: ConfigTemplate, 33 | ) -> Result + '_>> { 34 | let mut attrs = Vec::::with_capacity(32); 35 | 36 | // We use minimum to follow behavior of other platforms here. 37 | attrs.push(NSOpenGLPFAMinimumPolicy); 38 | 39 | // Allow offline renderers. 40 | attrs.push(NSOpenGLPFAAllowOfflineRenderers); 41 | 42 | // Color. 43 | match template.color_buffer_type { 44 | ColorBufferType::Rgb { r_size, g_size, b_size } => { 45 | attrs.push(NSOpenGLPFAColorSize); 46 | // We can't specify particular color, so we provide the sum, and also requires 47 | // an alpha. 48 | attrs.push((r_size + g_size + b_size + template.alpha_size) as u32); 49 | }, 50 | _ => { 51 | return Err(ErrorKind::NotSupported( 52 | "luminance buffers are not supported with CGL", 53 | ) 54 | .into()); 55 | }, 56 | } 57 | 58 | // Alpha. 59 | attrs.push(NSOpenGLPFAAlphaSize); 60 | attrs.push(template.alpha_size as u32); 61 | 62 | // Depth. 63 | attrs.push(NSOpenGLPFADepthSize); 64 | attrs.push(template.depth_size as u32); 65 | 66 | // Stencil. 67 | attrs.push(NSOpenGLPFAStencilSize); 68 | attrs.push(template.stencil_size as u32); 69 | 70 | // Float colors. 71 | if template.float_pixels { 72 | attrs.push(NSOpenGLPFAColorFloat); 73 | } 74 | 75 | // Sample buffers. 76 | if let Some(num_samples) = template.num_samples { 77 | attrs.push(NSOpenGLPFAMultisample); 78 | attrs.push(NSOpenGLPFASampleBuffers); 79 | attrs.push(1); 80 | attrs.push(NSOpenGLPFASamples); 81 | attrs.push(num_samples as u32); 82 | } 83 | 84 | // Double buffering. 85 | if !template.single_buffering { 86 | attrs.push(NSOpenGLPFADoubleBuffer); 87 | } 88 | 89 | if template.hardware_accelerated == Some(true) { 90 | attrs.push(NSOpenGLPFAAccelerated); 91 | } 92 | 93 | // Stereo. 94 | if template.stereoscopy == Some(true) { 95 | attrs.push(NSOpenGLPFAStereo); 96 | } 97 | 98 | attrs.push(NSOpenGLPFAOpenGLProfile); 99 | 100 | // Stash profile pos for latter insert. 101 | let profile_attr_pos = attrs.len(); 102 | // Add place holder for the GL profile. 103 | attrs.push(NSOpenGLProfileVersion4_1Core); 104 | 105 | // Terminate attrs with zero. 106 | attrs.push(0); 107 | 108 | // Automatically pick the latest profile. 109 | let raw = [ 110 | NSOpenGLProfileVersion4_1Core, 111 | NSOpenGLProfileVersion3_2Core, 112 | NSOpenGLProfileVersionLegacy, 113 | ] 114 | .into_iter() 115 | .find_map(|profile| { 116 | attrs[profile_attr_pos] = profile; 117 | // initWithAttributes returns None if the attributes were invalid 118 | unsafe { 119 | NSOpenGLPixelFormat::initWithAttributes( 120 | ::alloc(), 121 | NonNull::new(attrs.as_ptr().cast_mut()).unwrap(), 122 | ) 123 | } 124 | }) 125 | .ok_or(ErrorKind::BadConfig)?; 126 | 127 | let inner = Arc::new(ConfigInner { 128 | display: self.clone(), 129 | raw, 130 | transparency: template.transparency, 131 | }); 132 | let config = Config { inner }; 133 | 134 | Ok(Box::new(iter::once(config))) 135 | } 136 | } 137 | 138 | /// A wrapper around NSOpenGLPixelFormat. 139 | #[derive(Debug, Clone, PartialEq, Eq)] 140 | pub struct Config { 141 | pub(crate) inner: Arc, 142 | } 143 | 144 | impl Config { 145 | #[allow(deprecated)] 146 | fn raw_attribute(&self, attrib: NSOpenGLPixelFormatAttribute) -> i32 { 147 | unsafe { 148 | let mut value = 0; 149 | self.inner.raw.getValues_forAttribute_forVirtualScreen( 150 | NonNull::from(&mut value), 151 | attrib, 152 | // They do differ per monitor and require context. Which is kind of insane, but 153 | // whatever. Zero is a primary monitor. 154 | 0, 155 | ); 156 | value 157 | } 158 | } 159 | 160 | #[allow(deprecated)] 161 | pub(crate) fn is_single_buffered(&self) -> bool { 162 | self.raw_attribute(NSOpenGLPFATripleBuffer) == 0 163 | && self.raw_attribute(NSOpenGLPFADoubleBuffer) == 0 164 | } 165 | } 166 | 167 | #[allow(deprecated)] 168 | impl GlConfig for Config { 169 | fn color_buffer_type(&self) -> Option { 170 | // On macos all color formats divide by 3 without reminder, except for the RGB 171 | // 565. So we can convert it in a hopefully reliable way. Also we should remove 172 | // alpha. 173 | let color = self.raw_attribute(NSOpenGLPFAColorSize) - self.alpha_size() as i32; 174 | let r_size = (color / 3) as u8; 175 | let b_size = (color / 3) as u8; 176 | let g_size = (color - r_size as i32 - b_size as i32) as u8; 177 | Some(ColorBufferType::Rgb { r_size, g_size, b_size }) 178 | } 179 | 180 | fn float_pixels(&self) -> bool { 181 | self.raw_attribute(NSOpenGLPFAColorFloat) != 0 182 | } 183 | 184 | fn alpha_size(&self) -> u8 { 185 | self.raw_attribute(NSOpenGLPFAAlphaSize) as u8 186 | } 187 | 188 | fn srgb_capable(&self) -> bool { 189 | true 190 | } 191 | 192 | fn hardware_accelerated(&self) -> bool { 193 | self.raw_attribute(NSOpenGLPFAAccelerated) != 0 194 | } 195 | 196 | fn depth_size(&self) -> u8 { 197 | self.raw_attribute(NSOpenGLPFADepthSize) as u8 198 | } 199 | 200 | fn stencil_size(&self) -> u8 { 201 | self.raw_attribute(NSOpenGLPFAStencilSize) as u8 202 | } 203 | 204 | fn num_samples(&self) -> u8 { 205 | self.raw_attribute(NSOpenGLPFASamples) as u8 206 | } 207 | 208 | fn config_surface_types(&self) -> ConfigSurfaceTypes { 209 | ConfigSurfaceTypes::WINDOW 210 | } 211 | 212 | fn supports_transparency(&self) -> Option { 213 | Some(self.inner.transparency) 214 | } 215 | 216 | fn api(&self) -> Api { 217 | Api::OPENGL 218 | } 219 | } 220 | 221 | impl GetGlDisplay for Config { 222 | type Target = Display; 223 | 224 | fn display(&self) -> Self::Target { 225 | self.inner.display.clone() 226 | } 227 | } 228 | 229 | impl AsRawConfig for Config { 230 | fn raw_config(&self) -> RawConfig { 231 | RawConfig::Cgl(Retained::as_ptr(&self.inner.raw).cast()) 232 | } 233 | } 234 | 235 | impl Sealed for Config {} 236 | 237 | pub(crate) struct ConfigInner { 238 | display: Display, 239 | pub(crate) transparency: bool, 240 | #[allow(deprecated)] 241 | pub(crate) raw: Retained, 242 | } 243 | 244 | unsafe impl Send for ConfigInner {} 245 | unsafe impl Sync for ConfigInner {} 246 | 247 | impl PartialEq for ConfigInner { 248 | fn eq(&self, other: &Self) -> bool { 249 | self.raw == other.raw 250 | } 251 | } 252 | 253 | impl Eq for ConfigInner {} 254 | 255 | impl fmt::Debug for ConfigInner { 256 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 257 | f.debug_struct("Config").field("id", &self.raw).finish() 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /glutin-winit/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This library provides helpers for cross-platform [`glutin`] bootstrapping 2 | //! with [`winit`]. 3 | 4 | #![deny(rust_2018_idioms)] 5 | #![deny(rustdoc::broken_intra_doc_links)] 6 | #![deny(clippy::all)] 7 | #![deny(missing_debug_implementations)] 8 | #![deny(missing_docs)] 9 | #![cfg_attr(clippy, deny(warnings))] 10 | 11 | mod event_loop; 12 | mod window; 13 | 14 | use event_loop::GlutinEventLoop; 15 | pub use window::GlWindow; 16 | 17 | use std::error::Error; 18 | 19 | use glutin::config::{Config, ConfigTemplateBuilder}; 20 | use glutin::display::{Display, DisplayApiPreference}; 21 | #[cfg(x11_platform)] 22 | use glutin::platform::x11::X11GlConfigExt; 23 | use glutin::prelude::*; 24 | 25 | #[cfg(wgl_backend)] 26 | use raw_window_handle::HasWindowHandle; 27 | 28 | use raw_window_handle::RawWindowHandle; 29 | use winit::error::OsError; 30 | use winit::window::{Window, WindowAttributes}; 31 | 32 | #[cfg(x11_platform)] 33 | use winit::platform::x11::WindowAttributesExtX11; 34 | #[cfg(glx_backend)] 35 | use winit::platform::x11::register_xlib_error_hook; 36 | 37 | #[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] 38 | compile_error!("Please select at least one api backend"); 39 | 40 | pub(crate) mod private { 41 | /// Prevent traits from being implemented downstream, since those are used 42 | /// purely for documentation organization and simplify platform api 43 | /// implementation maintenance. 44 | pub trait Sealed {} 45 | } 46 | 47 | /// The helper to perform [`Display`] creation and OpenGL platform 48 | /// bootstrapping with the help of [`winit`] with little to no platform specific 49 | /// code. 50 | /// 51 | /// This is only required for the initial setup. If you want to create 52 | /// additional windows just use the [`finalize_window`] function and the 53 | /// configuration you've used either for the original window or picked with the 54 | /// existing [`Display`]. 55 | /// 56 | /// [`winit`]: winit 57 | /// [`Display`]: glutin::display::Display 58 | #[derive(Default, Debug, Clone)] 59 | pub struct DisplayBuilder { 60 | preference: ApiPreference, 61 | window_attributes: Option, 62 | } 63 | 64 | impl DisplayBuilder { 65 | /// Create new display builder. 66 | pub fn new() -> Self { 67 | Default::default() 68 | } 69 | 70 | /// The preference in picking the configuration. 71 | pub fn with_preference(mut self, preference: ApiPreference) -> Self { 72 | self.preference = preference; 73 | self 74 | } 75 | 76 | /// The window attributes to use when building a window. 77 | /// 78 | /// By default no window is created. 79 | pub fn with_window_attributes(mut self, window_attributes: Option) -> Self { 80 | self.window_attributes = window_attributes; 81 | self 82 | } 83 | 84 | /// Initialize the OpenGL platform and create a compatible window to use 85 | /// with it when the [`WindowAttributes`] was passed with 86 | /// [`Self::with_window_attributes()`]. It's optional, since on some 87 | /// platforms like `Android` it is not available early on, so you want to 88 | /// find configuration and later use it with the [`finalize_window`]. 89 | /// But if you don't care about such platform you can always pass 90 | /// [`WindowAttributes`]. 91 | /// 92 | /// # Api-specific 93 | /// 94 | /// **WGL:** - [`WindowAttributes`] **must** be passed in 95 | /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired, 96 | /// otherwise only builtin functions like `glClear` will be available. 97 | pub fn build( 98 | mut self, 99 | event_loop: &impl GlutinEventLoop, 100 | template_builder: ConfigTemplateBuilder, 101 | config_picker: Picker, 102 | ) -> Result<(Option, Config), Box> 103 | where 104 | Picker: FnOnce(Box + '_>) -> Config, 105 | { 106 | // XXX with WGL backend window should be created first. 107 | #[cfg(wgl_backend)] 108 | let window = if let Some(wa) = self.window_attributes.take() { 109 | Some(event_loop.create_window(wa)?) 110 | } else { 111 | None 112 | }; 113 | 114 | #[cfg(wgl_backend)] 115 | let raw_window_handle = window 116 | .as_ref() 117 | .and_then(|window| window.window_handle().ok()) 118 | .map(|handle| handle.as_raw()); 119 | #[cfg(not(wgl_backend))] 120 | let raw_window_handle = None; 121 | 122 | let gl_display = create_display(event_loop, self.preference, raw_window_handle)?; 123 | 124 | // XXX the native window must be passed to config picker when WGL is used 125 | // otherwise very limited OpenGL features will be supported. 126 | #[cfg(wgl_backend)] 127 | let template_builder = if let Some(raw_window_handle) = raw_window_handle { 128 | template_builder.compatible_with_native_window(raw_window_handle) 129 | } else { 130 | template_builder 131 | }; 132 | 133 | let template = template_builder.build(); 134 | 135 | let gl_config = unsafe { 136 | let configs = gl_display.find_configs(template)?; 137 | config_picker(configs) 138 | }; 139 | 140 | #[cfg(not(wgl_backend))] 141 | let window = if let Some(wa) = self.window_attributes.take() { 142 | Some(finalize_window(event_loop, wa, &gl_config)?) 143 | } else { 144 | None 145 | }; 146 | 147 | Ok((window, gl_config)) 148 | } 149 | } 150 | 151 | fn create_display( 152 | event_loop: &impl GlutinEventLoop, 153 | _api_preference: ApiPreference, 154 | _raw_window_handle: Option, 155 | ) -> Result> { 156 | #[cfg(egl_backend)] 157 | let _preference = DisplayApiPreference::Egl; 158 | 159 | #[cfg(glx_backend)] 160 | let _preference = DisplayApiPreference::Glx(Box::new(register_xlib_error_hook)); 161 | 162 | #[cfg(cgl_backend)] 163 | let _preference = DisplayApiPreference::Cgl; 164 | 165 | #[cfg(wgl_backend)] 166 | let _preference = DisplayApiPreference::Wgl(_raw_window_handle); 167 | 168 | #[cfg(all(egl_backend, glx_backend))] 169 | let _preference = match _api_preference { 170 | ApiPreference::PreferEgl => { 171 | DisplayApiPreference::EglThenGlx(Box::new(register_xlib_error_hook)) 172 | }, 173 | ApiPreference::FallbackEgl => { 174 | DisplayApiPreference::GlxThenEgl(Box::new(register_xlib_error_hook)) 175 | }, 176 | }; 177 | 178 | #[cfg(all(wgl_backend, egl_backend))] 179 | let _preference = match _api_preference { 180 | ApiPreference::PreferEgl => DisplayApiPreference::EglThenWgl(_raw_window_handle), 181 | ApiPreference::FallbackEgl => DisplayApiPreference::WglThenEgl(_raw_window_handle), 182 | }; 183 | 184 | let handle = event_loop.glutin_display_handle()?.as_raw(); 185 | unsafe { Ok(Display::new(handle, _preference)?) } 186 | } 187 | 188 | /// Finalize [`Window`] creation by applying the options from the [`Config`], be 189 | /// aware that it could remove incompatible options from the window builder like 190 | /// `transparency`, when the provided config doesn't support it. 191 | /// 192 | /// [`Window`]: winit::window::Window 193 | /// [`Config`]: glutin::config::Config 194 | pub fn finalize_window( 195 | event_loop: &impl GlutinEventLoop, 196 | mut attributes: WindowAttributes, 197 | gl_config: &Config, 198 | ) -> Result { 199 | // Disable transparency if the end config doesn't support it. 200 | if gl_config.supports_transparency() == Some(false) { 201 | attributes = attributes.with_transparent(false); 202 | } 203 | 204 | #[cfg(x11_platform)] 205 | let attributes = if let Some(x11_visual) = gl_config.x11_visual() { 206 | attributes.with_x11_visual(x11_visual.visual_id() as _) 207 | } else { 208 | attributes 209 | }; 210 | 211 | event_loop.create_window(attributes) 212 | } 213 | 214 | /// Simplified version of the [`DisplayApiPreference`] which is used to simplify 215 | /// cross platform window creation. 216 | /// 217 | /// To learn about platform differences the [`DisplayApiPreference`] variants. 218 | /// 219 | /// [`DisplayApiPreference`]: glutin::display::DisplayApiPreference 220 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 221 | pub enum ApiPreference { 222 | /// Prefer `EGL` over system provider like `GLX` and `WGL`. 223 | PreferEgl, 224 | 225 | /// Fallback to `EGL` when failed to create the system profile. 226 | /// 227 | /// This behavior is used by default. However consider using 228 | /// [`Self::PreferEgl`] if you don't care about missing EGL features. 229 | #[default] 230 | FallbackEgl, 231 | } 232 | -------------------------------------------------------------------------------- /glutin/src/api/egl/device.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to `EGLDevice`. 2 | 3 | use std::collections::HashSet; 4 | use std::ffi::CStr; 5 | use std::path::Path; 6 | use std::ptr; 7 | 8 | use glutin_egl_sys::egl; 9 | use glutin_egl_sys::egl::types::EGLDeviceEXT; 10 | 11 | use crate::error::{ErrorKind, Result}; 12 | 13 | use super::display::{CLIENT_EXTENSIONS, extensions_from_ptr, get_extensions}; 14 | use super::{EGL, Egl}; 15 | 16 | /// Wrapper for `EGLDevice`. 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | pub struct Device { 19 | inner: EGLDeviceEXT, 20 | extensions: HashSet<&'static str>, 21 | name: Option<&'static str>, 22 | vendor: Option<&'static str>, 23 | } 24 | 25 | // SAFETY: An EGLDevice is immutable and valid for the lifetime of the EGL 26 | // library. 27 | unsafe impl Send for Device {} 28 | unsafe impl Sync for Device {} 29 | 30 | impl Device { 31 | /// Query the available devices. 32 | /// 33 | /// This function returns [`Err`] if the `EGL_EXT_device_query` and 34 | /// `EGL_EXT_device_enumeration` or `EGL_EXT_device_base` extensions are 35 | /// not available. 36 | pub fn query_devices() -> Result> { 37 | let egl = match EGL.as_ref() { 38 | Some(egl) => egl, 39 | None => return Err(ErrorKind::NotFound.into()), 40 | }; 41 | 42 | let client_extensions = 43 | CLIENT_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); 44 | 45 | // Querying devices requires EGL_EXT_device_enumeration or EGL_EXT_device_base. 46 | if !client_extensions.contains("EGL_EXT_device_base") { 47 | if !client_extensions.contains("EGL_EXT_device_enumeration") { 48 | return Err(ErrorKind::NotSupported( 49 | "Enumerating devices is not supported by the EGL instance", 50 | ) 51 | .into()); 52 | } 53 | // EGL_EXT_device_enumeration depends on EGL_EXT_device_query, 54 | // so also check that just in case. 55 | if !client_extensions.contains("EGL_EXT_device_query") { 56 | return Err(ErrorKind::NotSupported( 57 | "EGL_EXT_device_enumeration without EGL_EXT_device_query, buggy driver?", 58 | ) 59 | .into()); 60 | } 61 | } 62 | 63 | let mut device_count = 0; 64 | 65 | if unsafe { 66 | // The specification states: 67 | // > An EGL_BAD_PARAMETER error is generated if is 68 | // > less than or equal to zero unless is NULL, or if 69 | // > is NULL. 70 | // 71 | // The error will never be generated since num_devices is a pointer 72 | // to the count being queried. Therefore there is no need to check 73 | // the error. 74 | egl.QueryDevicesEXT(0, ptr::null_mut(), &mut device_count) == egl::FALSE 75 | } { 76 | super::check_error()?; 77 | // On failure, EGL_FALSE is returned. 78 | return Err(ErrorKind::NotSupported("Querying device count failed").into()); 79 | } 80 | 81 | let mut devices = Vec::with_capacity(device_count as usize); 82 | 83 | unsafe { 84 | let mut count = device_count; 85 | if egl.QueryDevicesEXT(device_count, devices.as_mut_ptr(), &mut count) == egl::FALSE { 86 | super::check_error()?; 87 | // On failure, EGL_FALSE is returned. 88 | return Err(ErrorKind::NotSupported("Querying devices failed").into()); 89 | } 90 | 91 | // SAFETY: EGL has initialized the vector for the number of devices. 92 | devices.set_len(device_count as usize); 93 | } 94 | 95 | Ok(devices.into_iter().flat_map(|ptr| Device::from_ptr(egl, ptr))) 96 | } 97 | 98 | /// Get the device extensions supported by this device. 99 | /// 100 | /// These extensions are distinct from the display extensions and should not 101 | /// be used interchangeably. 102 | pub fn extensions(&self) -> &HashSet<&'static str> { 103 | &self.extensions 104 | } 105 | 106 | /// Get the name of the device. 107 | /// 108 | /// This function will return [`None`] if the `EGL_EXT_device_query_name` 109 | /// device extension is not available. 110 | pub fn name(&self) -> Option<&'static str> { 111 | self.name 112 | } 113 | 114 | /// Get the vendor of the device. 115 | /// 116 | /// This function will return [`None`] if the `EGL_EXT_device_query_name` 117 | /// device extension is not available. 118 | pub fn vendor(&self) -> Option<&'static str> { 119 | self.vendor 120 | } 121 | 122 | /// Get a raw handle to the `EGLDevice`. 123 | pub fn raw_device(&self) -> EGLDeviceEXT { 124 | self.inner 125 | } 126 | 127 | /// Get the DRM primary or render device node path for this 128 | /// [`EGLDeviceEXT`]. 129 | /// 130 | /// Requires the [`EGL_EXT_device_drm`] extension. 131 | /// 132 | /// If the [`EGL_EXT_device_drm_render_node`] extension is supported, this 133 | /// is guaranteed to return the **primary** device node path, or [`None`]. 134 | /// Consult [`Self::drm_render_device_node_path()`] to retrieve the 135 | /// **render** device node path. 136 | /// 137 | /// [`EGL_EXT_device_drm`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm.txt 138 | /// [`EGL_EXT_device_drm_render_node`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm_render_node.txt 139 | pub fn drm_device_node_path(&self) -> Option<&'static Path> { 140 | if !self.extensions.contains("EGL_EXT_device_drm") { 141 | return None; 142 | } 143 | 144 | // SAFETY: We pass a valid EGLDevice pointer, and validated that the enum name 145 | // is valid because the extension is present. 146 | unsafe { Self::query_string(self.raw_device(), egl::DRM_DEVICE_FILE_EXT) }.map(Path::new) 147 | } 148 | 149 | /// Get the DRM render device node path for this [`EGLDeviceEXT`]. 150 | /// 151 | /// Requires the [`EGL_EXT_device_drm_render_node`] extension. 152 | /// 153 | /// If the [`EGL_EXT_device_drm`] extension is supported in addition to 154 | /// [`EGL_EXT_device_drm_render_node`], 155 | /// consult [`Self::drm_device_node_path()`] to retrieve the **primary** 156 | /// device node path. 157 | /// 158 | /// [`EGL_EXT_device_drm`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm.txt 159 | /// [`EGL_EXT_device_drm_render_node`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm_render_node.txt 160 | pub fn drm_render_device_node_path(&self) -> Option<&'static Path> { 161 | if !self.extensions.contains("EGL_EXT_device_drm_render_node") { 162 | return None; 163 | } 164 | 165 | const EGL_DRM_RENDER_NODE_PATH_EXT: egl::types::EGLenum = 0x3377; 166 | // SAFETY: We pass a valid EGLDevice pointer, and validated that the enum name 167 | // is valid because the extension is present. 168 | unsafe { Self::query_string(self.raw_device(), EGL_DRM_RENDER_NODE_PATH_EXT) } 169 | .map(Path::new) 170 | } 171 | 172 | /// # Safety 173 | /// The caller must pass a valid `egl_device` pointer and must ensure that 174 | /// `name` is valid for this device, i.e. by guaranteeing that the 175 | /// extension that introduces it is present. 176 | /// 177 | /// The returned string is `'static` for the lifetime of the globally loaded 178 | /// EGL library in [`EGL`]. 179 | unsafe fn query_string( 180 | egl_device: EGLDeviceEXT, 181 | name: egl::types::EGLenum, 182 | ) -> Option<&'static str> { 183 | let egl = super::EGL.as_ref().unwrap(); 184 | 185 | // SAFETY: The caller has ensured the name is valid. 186 | let ptr = unsafe { egl.QueryDeviceStringEXT(egl_device, name as _) }; 187 | 188 | if ptr.is_null() { 189 | return None; 190 | } 191 | 192 | unsafe { CStr::from_ptr(ptr) }.to_str().ok() 193 | } 194 | 195 | pub(crate) fn from_ptr(egl: &Egl, ptr: EGLDeviceEXT) -> Result { 196 | // SAFETY: The EGL specification guarantees the returned string is 197 | // static and null terminated: 198 | // 199 | // > eglQueryDeviceStringEXT returns a pointer to a static, 200 | // > zero-terminated string describing some aspect of the specified 201 | // > EGLDeviceEXT. must be EGL_EXTENSIONS. 202 | let extensions = 203 | unsafe { extensions_from_ptr(egl.QueryDeviceStringEXT(ptr, egl::EXTENSIONS as _)) }; 204 | 205 | let (name, vendor) = if extensions.contains("EGL_EXT_device_query_name") { 206 | // SAFETY: RENDERER_EXT and VENDOR are valid strings for device string queries 207 | // if EGL_EXT_device_query_name. 208 | unsafe { 209 | (Self::query_string(ptr, egl::RENDERER_EXT), Self::query_string(ptr, egl::VENDOR)) 210 | } 211 | } else { 212 | (None, None) 213 | }; 214 | 215 | Ok(Self { inner: ptr, extensions, name, vendor }) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /glutin/src/api/wgl/display.rs: -------------------------------------------------------------------------------- 1 | //! WGL display initialization and extension loading. 2 | 3 | use std::borrow::Cow; 4 | use std::collections::HashSet; 5 | use std::ffi::{self, CStr, OsStr}; 6 | use std::os::windows::ffi::OsStrExt; 7 | use std::sync::Arc; 8 | use std::{env, fmt}; 9 | 10 | use glutin_wgl_sys::wgl; 11 | use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; 12 | use windows_sys::Win32::Foundation::HMODULE; 13 | use windows_sys::Win32::Graphics::Gdi::HDC; 14 | use windows_sys::Win32::System::LibraryLoader as dll_loader; 15 | 16 | use crate::config::ConfigTemplate; 17 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; 18 | use crate::error::{ErrorKind, Result}; 19 | use crate::prelude::*; 20 | use crate::private::Sealed; 21 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; 22 | 23 | use super::WglExtra; 24 | use super::config::Config; 25 | use super::context::NotCurrentContext; 26 | use super::surface::Surface; 27 | 28 | /// The name of the OpenGL DLL to use. 29 | pub(crate) static GLUTIN_WGL_OPENGL_DLL_ENV: &str = "GLUTIN_WGL_OPENGL_DLL"; 30 | 31 | /// A WGL display. 32 | #[derive(Debug, Clone)] 33 | pub struct Display { 34 | pub(crate) inner: Arc, 35 | } 36 | 37 | impl Display { 38 | /// Create WGL display. 39 | /// 40 | /// The `native_window` is used to perform extension loading. If it's not 41 | /// passed the OpenGL will be limited to what it can do, though, basic 42 | /// operations could still be performed. 43 | /// 44 | /// You can alter OpenGL DLL name by setting `GLUTIN_WGL_OPENGL_DLL` 45 | /// environment variable. The default one is `opengl32.dll`. 46 | /// 47 | /// # Safety 48 | /// 49 | /// The `native_window` must point to the valid platform window and have 50 | /// valid `hinstance`. 51 | pub unsafe fn new( 52 | display: RawDisplayHandle, 53 | native_window: Option, 54 | ) -> Result { 55 | if !matches!(display, RawDisplayHandle::Windows(..)) { 56 | return Err(ErrorKind::NotSupported("provided native display is not supported").into()); 57 | } 58 | 59 | let dll_name: Cow<'_, str> = env::var(GLUTIN_WGL_OPENGL_DLL_ENV) 60 | .map(Into::into) 61 | .unwrap_or_else(|_| Cow::Borrowed("opengl32.dll")); 62 | let name = OsStr::new(dll_name.as_ref()).encode_wide().chain(Some(0)).collect::>(); 63 | let lib_opengl32 = unsafe { dll_loader::LoadLibraryW(name.as_ptr()) }; 64 | if lib_opengl32 == 0 { 65 | return Err(ErrorKind::NotFound.into()); 66 | } 67 | 68 | // In case native window was provided init extra functions. 69 | let (wgl_extra, client_extensions) = 70 | if let Some(RawWindowHandle::Win32(window)) = native_window { 71 | unsafe { 72 | let (wgl_extra, client_extensions) = super::load_extra_functions( 73 | window.hinstance.unwrap().get() as _, 74 | window.hwnd.get() as _, 75 | )?; 76 | (Some(wgl_extra), client_extensions) 77 | } 78 | } else { 79 | (None, HashSet::new()) 80 | }; 81 | 82 | let features = Self::extract_display_features(&client_extensions); 83 | 84 | let inner = Arc::new(DisplayInner { lib_opengl32, wgl_extra, features, client_extensions }); 85 | 86 | Ok(Display { inner }) 87 | } 88 | 89 | fn extract_display_features(extensions: &HashSet<&'static str>) -> DisplayFeatures { 90 | let mut features = DisplayFeatures::empty(); 91 | 92 | features.set( 93 | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, 94 | extensions.contains("WGL_ARB_multisample"), 95 | ); 96 | 97 | features.set( 98 | DisplayFeatures::FLOAT_PIXEL_FORMAT, 99 | extensions.contains("WGL_ARB_pixel_format_float"), 100 | ); 101 | 102 | features.set( 103 | DisplayFeatures::SRGB_FRAMEBUFFERS, 104 | extensions.contains("WGL_ARB_framebuffer_sRGB") 105 | || extensions.contains("WGL_EXT_framebuffer_sRGB") 106 | || extensions.contains("WGL_EXT_colorspace"), 107 | ); 108 | 109 | features.set( 110 | DisplayFeatures::CREATE_ES_CONTEXT, 111 | extensions.contains("WGL_EXT_create_context_es2_profile") 112 | || extensions.contains("WGL_EXT_create_context_es_profile"), 113 | ); 114 | 115 | features.set(DisplayFeatures::SWAP_CONTROL, extensions.contains("WGL_EXT_swap_control")); 116 | 117 | features.set( 118 | DisplayFeatures::CONTEXT_ROBUSTNESS, 119 | extensions.contains("WGL_ARB_create_context_robustness"), 120 | ); 121 | 122 | features.set( 123 | DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, 124 | extensions.contains("WGL_ARB_context_flush_control"), 125 | ); 126 | 127 | features.set( 128 | DisplayFeatures::CONTEXT_NO_ERROR, 129 | extensions.contains("WGL_ARB_create_context_no_error"), 130 | ); 131 | 132 | features 133 | } 134 | } 135 | 136 | impl GlDisplay for Display { 137 | type Config = Config; 138 | type NotCurrentContext = NotCurrentContext; 139 | type PbufferSurface = Surface; 140 | type PixmapSurface = Surface; 141 | type WindowSurface = Surface; 142 | 143 | unsafe fn find_configs( 144 | &self, 145 | template: ConfigTemplate, 146 | ) -> Result + '_>> { 147 | unsafe { Self::find_configs(self, template) } 148 | } 149 | 150 | unsafe fn create_window_surface( 151 | &self, 152 | config: &Self::Config, 153 | surface_attributes: &SurfaceAttributes, 154 | ) -> Result { 155 | unsafe { Self::create_window_surface(self, config, surface_attributes) } 156 | } 157 | 158 | unsafe fn create_pbuffer_surface( 159 | &self, 160 | config: &Self::Config, 161 | surface_attributes: &SurfaceAttributes, 162 | ) -> Result { 163 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } 164 | } 165 | 166 | unsafe fn create_context( 167 | &self, 168 | config: &Self::Config, 169 | context_attributes: &crate::context::ContextAttributes, 170 | ) -> Result { 171 | unsafe { Self::create_context(self, config, context_attributes) } 172 | } 173 | 174 | unsafe fn create_pixmap_surface( 175 | &self, 176 | config: &Self::Config, 177 | surface_attributes: &SurfaceAttributes, 178 | ) -> Result { 179 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } 180 | } 181 | 182 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { 183 | unsafe { 184 | let addr = addr.as_ptr(); 185 | let fn_ptr = wgl::GetProcAddress(addr); 186 | if !fn_ptr.is_null() { 187 | fn_ptr.cast() 188 | } else { 189 | dll_loader::GetProcAddress(self.inner.lib_opengl32, addr.cast()) 190 | .map_or(std::ptr::null(), |fn_ptr| fn_ptr as *const _) 191 | } 192 | } 193 | } 194 | 195 | fn version_string(&self) -> String { 196 | String::from("WGL") 197 | } 198 | 199 | fn supported_features(&self) -> DisplayFeatures { 200 | self.inner.features 201 | } 202 | } 203 | 204 | impl GetDisplayExtensions for Display { 205 | fn extensions(&self) -> &HashSet<&'static str> { 206 | &self.inner.client_extensions 207 | } 208 | } 209 | 210 | impl AsRawDisplay for Display { 211 | fn raw_display(&self) -> RawDisplay { 212 | RawDisplay::Wgl 213 | } 214 | } 215 | 216 | impl Sealed for Display {} 217 | 218 | pub(crate) struct DisplayInner { 219 | /// Client WGL extensions. 220 | pub(crate) lib_opengl32: HMODULE, 221 | 222 | /// Extra functions used by the impl. 223 | pub(crate) wgl_extra: Option<&'static WglExtra>, 224 | 225 | pub(crate) features: DisplayFeatures, 226 | 227 | pub(crate) client_extensions: HashSet<&'static str>, 228 | } 229 | 230 | impl fmt::Debug for DisplayInner { 231 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 232 | f.debug_struct("Display") 233 | .field("features", &self.features) 234 | .field("extensions", &self.client_extensions) 235 | .finish() 236 | } 237 | } 238 | 239 | pub(crate) fn load_extensions(hdc: HDC, wgl_extra: &WglExtra) -> HashSet<&'static str> { 240 | let extensions = unsafe { 241 | if wgl_extra.GetExtensionsStringARB.is_loaded() { 242 | CStr::from_ptr(wgl_extra.GetExtensionsStringARB(hdc as *const _)) 243 | } else if wgl_extra.GetExtensionsStringEXT.is_loaded() { 244 | CStr::from_ptr(wgl_extra.GetExtensionsStringEXT()) 245 | } else { 246 | return HashSet::new(); 247 | } 248 | }; 249 | 250 | if let Ok(extensions) = extensions.to_str() { 251 | extensions.split(' ').collect::>() 252 | } else { 253 | HashSet::new() 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /glutin_egl_sys/src/egl.rs: -------------------------------------------------------------------------------- 1 | //! Manual implementation of EGL bindings. 2 | //! 3 | //! This is necessary since `gl_generator` is unmaintaned and incapable of 4 | //! generating bindings for some of the newer extensions. 5 | 6 | use std::ffi::c_uint; 7 | 8 | pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; 9 | pub type khronos_uint64_t = super::khronos_uint64_t; 10 | pub type khronos_ssize_t = super::khronos_ssize_t; 11 | pub type EGLNativeDisplayType = super::EGLNativeDisplayType; 12 | pub type EGLNativePixmapType = super::EGLNativePixmapType; 13 | pub type EGLNativeWindowType = super::EGLNativeWindowType; 14 | pub type EGLint = super::EGLint; 15 | pub type NativeDisplayType = super::EGLNativeDisplayType; 16 | pub type NativePixmapType = super::EGLNativePixmapType; 17 | pub type NativeWindowType = super::EGLNativeWindowType; 18 | 19 | include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); 20 | 21 | // EGL_EXT_platform_xcb 22 | pub const PLATFORM_XCB_EXT: super::EGLenum = 0x31DC; 23 | pub const PLATFORM_XCB_SCREEN_EXT: super::EGLenum = 0x31DE; 24 | // EGL_EXT_device_query_name 25 | pub const RENDERER_EXT: super::EGLenum = 0x335F; 26 | // EGL_ANGLE_platform_angle - https://chromium.googlesource.com/angle/angle/+/HEAD/extensions/EGL_ANGLE_platform_angle.txt 27 | pub const PLATFORM_ANGLE_ANGLE: super::EGLenum = 0x3202; 28 | pub const PLATFORM_ANGLE_TYPE_ANGLE: super::EGLenum = 0x3203; 29 | pub const PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: super::EGLenum = 0x3450; 30 | pub const PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE: super::EGLenum = 0x3204; 31 | pub const PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE: super::EGLenum = 0x3205; 32 | pub const PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: super::EGLenum = 0x3451; 33 | pub const PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: super::EGLenum = 0x348F; 34 | pub const PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: super::EGLenum = 0x3206; 35 | pub const PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: super::EGLenum = 0x320A; 36 | pub const PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: super::EGLenum = 0x345E; 37 | 38 | mod wayland_storage { 39 | use super::FnPtr; 40 | use super::__gl_imports::raw; 41 | 42 | // EGL_WL_create_wayland_buffer_from_image 43 | pub static mut CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL: FnPtr = 44 | FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; 45 | 46 | // EGL_WL_bind_wayland_display 47 | pub static mut BIND_WAYLAND_DISPLAY_WL: FnPtr = 48 | FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; 49 | pub static mut UNBIND_WAYLAND_DISPLAY_WL: FnPtr = 50 | FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; 51 | pub static mut QUERY_WAYLAND_BUFFER_WL: FnPtr = 52 | FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; 53 | } 54 | 55 | impl Egl { 56 | #[allow(non_snake_case, unused_variables, dead_code)] 57 | #[inline] 58 | pub unsafe fn CreateWaylandBufferFromImageWL( 59 | &self, 60 | dpy: types::EGLDisplay, 61 | image: types::EGLImageKHR, 62 | ) -> *mut std::ffi::c_void { 63 | __gl_imports::mem::transmute::< 64 | _, 65 | extern "system" fn(types::EGLDisplay, types::EGLImageKHR) -> *mut std::ffi::c_void, 66 | >(wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL.f)(dpy, image) 67 | } 68 | 69 | #[allow(non_snake_case, unused_variables, dead_code)] 70 | #[inline] 71 | pub unsafe fn BindWaylandDisplayWL( 72 | &self, 73 | dpy: types::EGLDisplay, 74 | display: *mut __gl_imports::raw::c_void, 75 | ) -> types::EGLBoolean { 76 | __gl_imports::mem::transmute::< 77 | _, 78 | extern "system" fn( 79 | types::EGLDisplay, 80 | *mut __gl_imports::raw::c_void, 81 | ) -> types::EGLBoolean, 82 | >(wayland_storage::BIND_WAYLAND_DISPLAY_WL.f)(dpy, display) 83 | } 84 | 85 | #[allow(non_snake_case, unused_variables, dead_code)] 86 | #[inline] 87 | pub unsafe fn UnbindWaylandDisplayWL( 88 | &self, 89 | dpy: types::EGLDisplay, 90 | display: *mut __gl_imports::raw::c_void, 91 | ) -> types::EGLBoolean { 92 | __gl_imports::mem::transmute::< 93 | _, 94 | extern "system" fn( 95 | types::EGLDisplay, 96 | *mut __gl_imports::raw::c_void, 97 | ) -> types::EGLBoolean, 98 | >(wayland_storage::UNBIND_WAYLAND_DISPLAY_WL.f)(dpy, display) 99 | } 100 | 101 | #[allow(non_snake_case, unused_variables, dead_code)] 102 | #[inline] 103 | pub unsafe fn QueryWaylandBufferWL( 104 | &self, 105 | dpy: types::EGLDisplay, 106 | buffer: *mut __gl_imports::raw::c_void, 107 | attribute: types::EGLint, 108 | value: *mut types::EGLint, 109 | ) -> types::EGLBoolean { 110 | __gl_imports::mem::transmute::< 111 | _, 112 | extern "system" fn( 113 | types::EGLDisplay, 114 | *mut __gl_imports::raw::c_void, 115 | types::EGLint, 116 | *mut types::EGLint, 117 | ) -> types::EGLBoolean, 118 | >(wayland_storage::QUERY_WAYLAND_BUFFER_WL.f)(dpy, buffer, attribute, value) 119 | } 120 | } 121 | 122 | // Extension: EGL_WL_create_wayland_buffer_from_image 123 | // 124 | 125 | #[allow(non_snake_case)] 126 | pub mod CreateWaylandBufferFromImageWL { 127 | use super::__gl_imports::raw; 128 | use super::{metaloadfn, wayland_storage, FnPtr}; 129 | 130 | #[inline] 131 | #[allow(dead_code)] 132 | pub fn is_loaded() -> bool { 133 | unsafe { wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL.is_loaded } 134 | } 135 | 136 | #[allow(dead_code)] 137 | pub fn load_with(mut loadfn: F) 138 | where 139 | F: FnMut(&'static str) -> *const raw::c_void, 140 | { 141 | unsafe { 142 | wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL = 143 | FnPtr::new(metaloadfn(&mut loadfn, "eglCreateWaylandBufferFromImageWL", &[])) 144 | } 145 | } 146 | } 147 | 148 | // Extension: EGL_WL_bind_wayland_display 149 | // 150 | 151 | // Accepted as in eglCreateImageKHR. 152 | pub const WAYLAND_BUFFER_WL: c_uint = 0x31D5; 153 | // Accepted in the parameter of eglCreateImageKHR. 154 | pub const WAYLAND_PLANE_WL: c_uint = 0x31D6; 155 | // Possible values for EGL_TEXTURE_FORMAT. 156 | pub const TEXTURE_Y_U_V_WL: i32 = 0x31D7; 157 | pub const TEXTURE_Y_UV_WL: i32 = 0x31D8; 158 | pub const TEXTURE_Y_XUXV_WL: i32 = 0x31D9; 159 | pub const TEXTURE_EXTERNAL_WL: i32 = 0x31DA; 160 | pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB; 161 | 162 | #[allow(non_snake_case)] 163 | pub mod BindWaylandDisplayWL { 164 | use super::__gl_imports::raw; 165 | use super::{metaloadfn, wayland_storage, FnPtr}; 166 | 167 | #[inline] 168 | #[allow(dead_code)] 169 | pub fn is_loaded() -> bool { 170 | unsafe { wayland_storage::BIND_WAYLAND_DISPLAY_WL.is_loaded } 171 | } 172 | 173 | #[allow(dead_code)] 174 | pub fn load_with(mut loadfn: F) 175 | where 176 | F: FnMut(&'static str) -> *const raw::c_void, 177 | { 178 | unsafe { 179 | wayland_storage::BIND_WAYLAND_DISPLAY_WL = 180 | FnPtr::new(metaloadfn(&mut loadfn, "eglBindWaylandDisplayWL", &[])) 181 | } 182 | } 183 | } 184 | 185 | #[allow(non_snake_case)] 186 | pub mod UnbindWaylandDisplayWL { 187 | use super::__gl_imports::raw; 188 | use super::{metaloadfn, wayland_storage, FnPtr}; 189 | 190 | #[inline] 191 | #[allow(dead_code)] 192 | pub fn is_loaded() -> bool { 193 | unsafe { wayland_storage::UNBIND_WAYLAND_DISPLAY_WL.is_loaded } 194 | } 195 | 196 | #[allow(dead_code)] 197 | pub fn load_with(mut loadfn: F) 198 | where 199 | F: FnMut(&'static str) -> *const raw::c_void, 200 | { 201 | unsafe { 202 | wayland_storage::UNBIND_WAYLAND_DISPLAY_WL = 203 | FnPtr::new(metaloadfn(&mut loadfn, "eglUnbindWaylandDisplayWL", &[])) 204 | } 205 | } 206 | } 207 | 208 | #[allow(non_snake_case)] 209 | pub mod QueryWaylandBufferWL { 210 | use super::__gl_imports::raw; 211 | use super::{metaloadfn, wayland_storage, FnPtr}; 212 | 213 | #[inline] 214 | #[allow(dead_code)] 215 | pub fn is_loaded() -> bool { 216 | unsafe { wayland_storage::QUERY_WAYLAND_BUFFER_WL.is_loaded } 217 | } 218 | 219 | #[allow(dead_code)] 220 | pub fn load_with(mut loadfn: F) 221 | where 222 | F: FnMut(&'static str) -> *const raw::c_void, 223 | { 224 | unsafe { 225 | wayland_storage::QUERY_WAYLAND_BUFFER_WL = 226 | FnPtr::new(metaloadfn(&mut loadfn, "eglQueryWaylandBufferWL", &[])) 227 | } 228 | } 229 | } 230 | 231 | /// OpenGL function loader. 232 | /// 233 | /// This is based on the loader generated by `gl_generator`. 234 | #[inline(never)] 235 | fn metaloadfn( 236 | loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void, 237 | symbol: &'static str, 238 | fallbacks: &[&'static str], 239 | ) -> *const __gl_imports::raw::c_void { 240 | let mut ptr = loadfn(symbol); 241 | if ptr.is_null() { 242 | for &sym in fallbacks { 243 | ptr = loadfn(sym); 244 | if !ptr.is_null() { 245 | break; 246 | } 247 | } 248 | } 249 | ptr 250 | } 251 | -------------------------------------------------------------------------------- /glutin/src/api/cgl/context.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to `NSOpenGLContext`. 2 | 3 | use std::fmt; 4 | use std::marker::PhantomData; 5 | use std::ptr::NonNull; 6 | 7 | use dispatch2::{MainThreadBound, run_on_main}; 8 | use objc2::AnyThread; 9 | use objc2::rc::{Retained, autoreleasepool}; 10 | #[allow(deprecated)] 11 | use objc2_app_kit::{NSOpenGLCPSwapInterval, NSOpenGLContext, NSView}; 12 | #[allow(deprecated)] 13 | use objc2_open_gl::{CGLContextParameter, CGLSetParameter}; 14 | 15 | use crate::config::GetGlConfig; 16 | use crate::context::{ 17 | AsRawContext, ContextApi, ContextAttributes, Priority, RawContext, Robustness, 18 | }; 19 | use crate::display::GetGlDisplay; 20 | use crate::error::{ErrorKind, Result}; 21 | use crate::prelude::*; 22 | use crate::private::Sealed; 23 | use crate::surface::{SurfaceTypeTrait, SwapInterval}; 24 | 25 | use super::config::Config; 26 | use super::display::Display; 27 | use super::surface::Surface; 28 | 29 | impl Display { 30 | pub(crate) unsafe fn create_context( 31 | &self, 32 | config: &Config, 33 | context_attributes: &ContextAttributes, 34 | ) -> Result { 35 | #[allow(deprecated)] 36 | let share_context = match context_attributes.shared_context.as_ref() { 37 | Some(RawContext::Cgl(share_context)) => unsafe { 38 | share_context.cast::().as_ref() 39 | }, 40 | _ => None, 41 | }; 42 | 43 | if matches!(context_attributes.api, Some(ContextApi::Gles(_))) { 44 | return Err(ErrorKind::NotSupported("gles is not supported with CGL").into()); 45 | } 46 | 47 | if context_attributes.robustness != Robustness::NotRobust { 48 | return Err(ErrorKind::NotSupported("robustness is not supported with CGL").into()); 49 | } 50 | 51 | let config = config.clone(); 52 | #[allow(deprecated)] 53 | let raw = NSOpenGLContext::initWithFormat_shareContext( 54 | NSOpenGLContext::alloc(), 55 | &config.inner.raw, 56 | share_context, 57 | ) 58 | .ok_or(ErrorKind::BadConfig)?; 59 | 60 | #[allow(deprecated)] 61 | if config.inner.transparency { 62 | let opacity = 0; 63 | super::check_error(unsafe { 64 | CGLSetParameter( 65 | raw.CGLContextObj().cast(), 66 | CGLContextParameter::CGLCPSurfaceOpacity, 67 | NonNull::from(&opacity), 68 | ) 69 | })?; 70 | } 71 | 72 | let inner = ContextInner { display: self.clone(), config, raw }; 73 | let context = NotCurrentContext::new(inner); 74 | 75 | Ok(context) 76 | } 77 | } 78 | 79 | /// A wrapper arounh `NSOpenGLContext` that is known to be not current on the 80 | /// current thread. 81 | #[derive(Debug)] 82 | pub struct NotCurrentContext { 83 | pub(crate) inner: ContextInner, 84 | _nosync: PhantomData>, 85 | } 86 | 87 | impl NotCurrentContext { 88 | fn new(inner: ContextInner) -> Self { 89 | Self { inner, _nosync: PhantomData } 90 | } 91 | } 92 | 93 | impl NotCurrentGlContext for NotCurrentContext { 94 | type PossiblyCurrentContext = PossiblyCurrentContext; 95 | type Surface = Surface; 96 | 97 | fn treat_as_possibly_current(self) -> PossiblyCurrentContext { 98 | PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } 99 | } 100 | 101 | fn make_current( 102 | self, 103 | surface: &Self::Surface, 104 | ) -> Result { 105 | self.inner.make_current(surface)?; 106 | Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) 107 | } 108 | 109 | fn make_current_draw_read( 110 | self, 111 | surface_draw: &Self::Surface, 112 | surface_read: &Self::Surface, 113 | ) -> Result { 114 | Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) 115 | } 116 | 117 | fn make_current_surfaceless(self) -> Result { 118 | self.inner.make_current_surfaceless()?; 119 | Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) 120 | } 121 | } 122 | 123 | impl GlContext for NotCurrentContext { 124 | fn context_api(&self) -> ContextApi { 125 | self.inner.context_api() 126 | } 127 | 128 | fn priority(&self) -> Priority { 129 | Priority::Medium 130 | } 131 | } 132 | 133 | impl GetGlConfig for NotCurrentContext { 134 | type Target = Config; 135 | 136 | fn config(&self) -> Self::Target { 137 | self.inner.config.clone() 138 | } 139 | } 140 | 141 | impl GetGlDisplay for NotCurrentContext { 142 | type Target = Display; 143 | 144 | fn display(&self) -> Self::Target { 145 | self.inner.display.clone() 146 | } 147 | } 148 | 149 | impl AsRawContext for NotCurrentContext { 150 | fn raw_context(&self) -> RawContext { 151 | RawContext::Cgl(Retained::as_ptr(&self.inner.raw).cast()) 152 | } 153 | } 154 | 155 | impl Sealed for NotCurrentContext {} 156 | 157 | /// A wrapper around `NSOpenGLContext` that could be curront on the current 158 | /// thread. 159 | #[derive(Debug)] 160 | pub struct PossiblyCurrentContext { 161 | pub(crate) inner: ContextInner, 162 | // The context could be current only on the one thread. 163 | _nosendsync: PhantomData<*mut ()>, 164 | } 165 | 166 | impl PossiblyCurrentGlContext for PossiblyCurrentContext { 167 | type NotCurrentContext = NotCurrentContext; 168 | type Surface = Surface; 169 | 170 | fn make_not_current(self) -> Result { 171 | self.make_not_current_in_place()?; 172 | Ok(NotCurrentContext::new(self.inner)) 173 | } 174 | 175 | fn make_not_current_in_place(&self) -> Result<()> { 176 | self.inner.make_not_current() 177 | } 178 | 179 | #[allow(deprecated)] 180 | fn is_current(&self) -> bool { 181 | if let Some(current) = NSOpenGLContext::currentContext() { 182 | current == self.inner.raw 183 | } else { 184 | false 185 | } 186 | } 187 | 188 | fn make_current(&self, surface: &Self::Surface) -> Result<()> { 189 | self.inner.make_current(surface) 190 | } 191 | 192 | fn make_current_draw_read( 193 | &self, 194 | surface_draw: &Self::Surface, 195 | surface_read: &Self::Surface, 196 | ) -> Result<()> { 197 | Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) 198 | } 199 | 200 | fn make_current_surfaceless(&self) -> Result<()> { 201 | self.inner.make_current_surfaceless() 202 | } 203 | } 204 | 205 | impl GlContext for PossiblyCurrentContext { 206 | fn context_api(&self) -> ContextApi { 207 | self.inner.context_api() 208 | } 209 | 210 | fn priority(&self) -> Priority { 211 | Priority::Medium 212 | } 213 | } 214 | 215 | impl GetGlConfig for PossiblyCurrentContext { 216 | type Target = Config; 217 | 218 | fn config(&self) -> Self::Target { 219 | self.inner.config.clone() 220 | } 221 | } 222 | 223 | impl GetGlDisplay for PossiblyCurrentContext { 224 | type Target = Display; 225 | 226 | fn display(&self) -> Self::Target { 227 | self.inner.display.clone() 228 | } 229 | } 230 | 231 | impl AsRawContext for PossiblyCurrentContext { 232 | fn raw_context(&self) -> RawContext { 233 | RawContext::Cgl(Retained::as_ptr(&self.inner.raw).cast()) 234 | } 235 | } 236 | 237 | impl Sealed for PossiblyCurrentContext {} 238 | 239 | pub(crate) struct ContextInner { 240 | display: Display, 241 | config: Config, 242 | #[allow(deprecated)] 243 | pub(crate) raw: Retained, 244 | } 245 | 246 | unsafe impl Send for ContextInner {} 247 | unsafe impl Sync for ContextInner {} 248 | 249 | impl ContextInner { 250 | fn make_current_draw_read( 251 | &self, 252 | _surface_draw: &Surface, 253 | _surface_read: &Surface, 254 | ) -> ErrorKind { 255 | ErrorKind::NotSupported("make current draw read isn't supported with CGL") 256 | } 257 | 258 | fn make_current(&self, surface: &Surface) -> Result<()> { 259 | autoreleasepool(|_| { 260 | self.update(); 261 | self.raw.makeCurrentContext(); 262 | 263 | let view = &surface.ns_view; 264 | #[allow(deprecated)] 265 | run_on_main(|mtm| self.raw.setView(Some(view.get(mtm)), mtm)); 266 | 267 | Ok(()) 268 | }) 269 | } 270 | 271 | fn make_current_surfaceless(&self) -> Result<()> { 272 | autoreleasepool(|_| { 273 | self.update(); 274 | self.raw.makeCurrentContext(); 275 | 276 | #[allow(deprecated)] 277 | run_on_main(|mtm| self.raw.setView(None, mtm)); 278 | 279 | Ok(()) 280 | }) 281 | } 282 | 283 | fn context_api(&self) -> ContextApi { 284 | ContextApi::OpenGl(None) 285 | } 286 | 287 | pub(crate) fn set_swap_interval(&self, interval: SwapInterval) { 288 | let interval = match interval { 289 | SwapInterval::DontWait => 0, 290 | SwapInterval::Wait(_) => 1, 291 | }; 292 | 293 | #[allow(deprecated)] 294 | autoreleasepool(|_| unsafe { 295 | self.raw.setValues_forParameter(NonNull::from(&interval), NSOpenGLCPSwapInterval); 296 | }) 297 | } 298 | 299 | pub(crate) fn update(&self) { 300 | run_on_main(|mtm| self.raw.update(mtm)); 301 | } 302 | 303 | pub(crate) fn flush_buffer(&self) -> Result<()> { 304 | autoreleasepool(|_| { 305 | self.raw.flushBuffer(); 306 | Ok(()) 307 | }) 308 | } 309 | 310 | #[allow(deprecated)] 311 | pub(crate) fn is_view_current(&self, view: &MainThreadBound>) -> bool { 312 | run_on_main(|mtm| { 313 | self.raw.view(mtm).expect("context to have a current view") == *view.get(mtm) 314 | }) 315 | } 316 | 317 | #[allow(deprecated)] 318 | fn make_not_current(&self) -> Result<()> { 319 | self.update(); 320 | NSOpenGLContext::clearCurrentContext(); 321 | Ok(()) 322 | } 323 | } 324 | 325 | impl fmt::Debug for ContextInner { 326 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 327 | f.debug_struct("Context") 328 | .field("config", &self.config.inner.raw) 329 | .field("raw", &self.raw) 330 | .finish() 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /glutin/src/api/glx/display.rs: -------------------------------------------------------------------------------- 1 | //! GLX object creation. 2 | 3 | use std::collections::HashSet; 4 | use std::ffi::{self, CStr}; 5 | use std::fmt; 6 | use std::ops::Deref; 7 | use std::sync::Arc; 8 | use std::sync::atomic::Ordering; 9 | 10 | use glutin_glx_sys::glx; 11 | use glutin_glx_sys::glx::types::Display as GLXDisplay; 12 | use raw_window_handle::RawDisplayHandle; 13 | 14 | use crate::config::ConfigTemplate; 15 | use crate::context::Version; 16 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; 17 | use crate::error::{ErrorKind, Result}; 18 | use crate::prelude::*; 19 | use crate::private::Sealed; 20 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; 21 | 22 | use super::config::Config; 23 | use super::context::NotCurrentContext; 24 | use super::surface::Surface; 25 | use super::{GLX, GLX_BASE_ERROR, GLX_EXTRA, Glx, GlxExtra, XlibErrorHookRegistrar}; 26 | 27 | /// A wrapper for the `GLXDisplay`, which is basically an `XDisplay`. 28 | #[derive(Debug, Clone)] 29 | pub struct Display { 30 | pub(crate) inner: Arc, 31 | } 32 | 33 | impl Display { 34 | /// Create GLX display. 35 | /// 36 | /// # Safety 37 | /// 38 | /// The `display` must point to the valid Xlib display and 39 | /// `error_hook_registrar` must be registered in your Xlib error handling 40 | /// callback. 41 | pub unsafe fn new( 42 | display: RawDisplayHandle, 43 | error_hook_registrar: XlibErrorHookRegistrar, 44 | ) -> Result { 45 | // Don't load GLX when unsupported platform was requested. 46 | let (display, screen) = match display { 47 | RawDisplayHandle::Xlib(handle) => match handle.display { 48 | Some(display) => (GlxDisplay(display.as_ptr() as *mut _), handle.screen as i32), 49 | None => return Err(ErrorKind::BadDisplay.into()), 50 | }, 51 | _ => { 52 | return Err( 53 | ErrorKind::NotSupported("provided native display isn't supported").into() 54 | ); 55 | }, 56 | }; 57 | 58 | let glx = match GLX.as_ref() { 59 | Some(glx) => glx, 60 | None => return Err(ErrorKind::NotFound.into()), 61 | }; 62 | 63 | // Set the base for errors coming from GLX. 64 | unsafe { 65 | let mut error_base = 0; 66 | let mut event_base = 0; 67 | if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { 68 | // The glx extension isn't present. 69 | return Err(ErrorKind::InitializationFailed.into()); 70 | } 71 | GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); 72 | } 73 | 74 | // This is completely ridiculous, but VirtualBox's OpenGL driver needs 75 | // some call handled by *it* (i.e. not Mesa) to occur before 76 | // anything else can happen. That is because VirtualBox's OpenGL 77 | // driver is going to apply binary patches to Mesa in the DLL 78 | // constructor and until it's loaded it won't have a chance to do that. 79 | // 80 | // The easiest way to do this is to just call `glXQueryVersion()` before 81 | // doing anything else. See: https://www.virtualbox.org/ticket/8293 82 | let version = unsafe { 83 | let (mut major, mut minor) = (0, 0); 84 | if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { 85 | return Err(ErrorKind::InitializationFailed.into()); 86 | } 87 | Version::new(major as u8, minor as u8) 88 | }; 89 | 90 | if version < Version::new(1, 3) { 91 | return Err(ErrorKind::NotSupported("the glx below 1.3 isn't supported").into()); 92 | } 93 | 94 | // Register the error handling hook. 95 | error_hook_registrar(Box::new(super::glx_error_hook)); 96 | 97 | let client_extensions = get_extensions(glx, display); 98 | let features = Self::extract_display_features(&client_extensions, version); 99 | 100 | let inner = Arc::new(DisplayInner { 101 | raw: display, 102 | glx, 103 | glx_extra: GLX_EXTRA.as_ref(), 104 | version, 105 | screen, 106 | features, 107 | client_extensions, 108 | }); 109 | 110 | Ok(Self { inner }) 111 | } 112 | 113 | /// Get a reference to the initialized GLX API. 114 | pub fn glx(&self) -> &'static Glx { 115 | self.inner.glx 116 | } 117 | 118 | fn extract_display_features( 119 | extensions: &HashSet<&'static str>, 120 | version: Version, 121 | ) -> DisplayFeatures { 122 | let mut features = DisplayFeatures::empty(); 123 | 124 | features.set( 125 | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, 126 | version >= Version::new(1, 4) || extensions.contains("GLX_ARB_multisample"), 127 | ); 128 | 129 | features.set( 130 | DisplayFeatures::FLOAT_PIXEL_FORMAT, 131 | extensions.contains("GLX_ARB_fbconfig_float"), 132 | ); 133 | 134 | features.set( 135 | DisplayFeatures::SRGB_FRAMEBUFFERS, 136 | extensions.contains("GLX_ARB_framebuffer_sRGB") 137 | || extensions.contains("GLX_EXT_framebuffer_sRGB"), 138 | ); 139 | 140 | features.set( 141 | DisplayFeatures::CREATE_ES_CONTEXT, 142 | extensions.contains("GLX_EXT_create_context_es2_profile") 143 | || extensions.contains("GLX_EXT_create_context_es_profile"), 144 | ); 145 | 146 | features.set( 147 | DisplayFeatures::SWAP_CONTROL, 148 | extensions.contains("GLX_EXT_swap_control") 149 | || extensions.contains("GLX_SGI_swap_control") 150 | || extensions.contains("GLX_MESA_swap_control"), 151 | ); 152 | 153 | features.set( 154 | DisplayFeatures::CONTEXT_ROBUSTNESS, 155 | extensions.contains("GLX_ARB_create_context_robustness"), 156 | ); 157 | 158 | features.set( 159 | DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, 160 | extensions.contains("GLX_ARB_context_flush_control"), 161 | ); 162 | 163 | features.set( 164 | DisplayFeatures::CONTEXT_NO_ERROR, 165 | extensions.contains("GLX_ARB_create_context_no_error"), 166 | ); 167 | 168 | features 169 | } 170 | } 171 | 172 | impl GlDisplay for Display { 173 | type Config = Config; 174 | type NotCurrentContext = NotCurrentContext; 175 | type PbufferSurface = Surface; 176 | type PixmapSurface = Surface; 177 | type WindowSurface = Surface; 178 | 179 | unsafe fn find_configs( 180 | &self, 181 | template: ConfigTemplate, 182 | ) -> Result + '_>> { 183 | unsafe { Self::find_configs(self, template) } 184 | } 185 | 186 | unsafe fn create_window_surface( 187 | &self, 188 | config: &Self::Config, 189 | surface_attributes: &SurfaceAttributes, 190 | ) -> Result { 191 | unsafe { Self::create_window_surface(self, config, surface_attributes) } 192 | } 193 | 194 | unsafe fn create_pbuffer_surface( 195 | &self, 196 | config: &Self::Config, 197 | surface_attributes: &SurfaceAttributes, 198 | ) -> Result { 199 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } 200 | } 201 | 202 | unsafe fn create_context( 203 | &self, 204 | config: &Self::Config, 205 | context_attributes: &crate::context::ContextAttributes, 206 | ) -> Result { 207 | unsafe { Self::create_context(self, config, context_attributes) } 208 | } 209 | 210 | unsafe fn create_pixmap_surface( 211 | &self, 212 | config: &Self::Config, 213 | surface_attributes: &SurfaceAttributes, 214 | ) -> Result { 215 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } 216 | } 217 | 218 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { 219 | unsafe { self.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ } 220 | } 221 | 222 | fn version_string(&self) -> String { 223 | format!("GLX {}.{}", self.inner.version.major, self.inner.version.minor) 224 | } 225 | 226 | fn supported_features(&self) -> DisplayFeatures { 227 | self.inner.features 228 | } 229 | } 230 | 231 | impl GetDisplayExtensions for Display { 232 | fn extensions(&self) -> &HashSet<&'static str> { 233 | &self.inner.client_extensions 234 | } 235 | } 236 | 237 | impl AsRawDisplay for Display { 238 | fn raw_display(&self) -> RawDisplay { 239 | RawDisplay::Glx(self.inner.raw.cast()) 240 | } 241 | } 242 | 243 | impl Sealed for Display {} 244 | 245 | pub(crate) struct DisplayInner { 246 | pub(crate) glx: &'static Glx, 247 | pub(crate) glx_extra: Option<&'static GlxExtra>, 248 | pub(crate) raw: GlxDisplay, 249 | pub(crate) screen: i32, 250 | pub(crate) version: Version, 251 | pub(crate) features: DisplayFeatures, 252 | /// Client GLX extensions. 253 | pub(crate) client_extensions: HashSet<&'static str>, 254 | } 255 | 256 | impl fmt::Debug for DisplayInner { 257 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 258 | f.debug_struct("Display") 259 | .field("raw", &self.raw) 260 | .field("version", &self.version) 261 | .field("screen", &self.screen) 262 | .field("features", &self.features) 263 | .field("extensions", &self.client_extensions) 264 | .finish() 265 | } 266 | } 267 | 268 | #[derive(Debug, Clone, Copy)] 269 | pub(crate) struct GlxDisplay(*mut GLXDisplay); 270 | 271 | unsafe impl Send for GlxDisplay {} 272 | unsafe impl Sync for GlxDisplay {} 273 | 274 | impl Deref for GlxDisplay { 275 | type Target = *mut GLXDisplay; 276 | 277 | fn deref(&self) -> &Self::Target { 278 | &self.0 279 | } 280 | } 281 | 282 | /// Load the GLX extensions. 283 | fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> { 284 | unsafe { 285 | let extensions = glx.GetClientString(display.0, glx::EXTENSIONS as i32); 286 | if extensions.is_null() { 287 | return HashSet::new(); 288 | } 289 | 290 | if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { 291 | extensions.split(' ').collect::>() 292 | } else { 293 | HashSet::new() 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /glutin/src/api/wgl/surface.rs: -------------------------------------------------------------------------------- 1 | //! A wrapper around `HWND` used for GL operations. 2 | 3 | use std::io::Error as IoError; 4 | use std::marker::PhantomData; 5 | use std::num::NonZeroU32; 6 | use std::os::raw::c_int; 7 | use std::{fmt, mem}; 8 | 9 | use glutin_wgl_sys::wgl::types::GLenum; 10 | use glutin_wgl_sys::wgl_extra::types::HPBUFFEREXT; 11 | use glutin_wgl_sys::wgl_extra::{self}; 12 | use raw_window_handle::RawWindowHandle; 13 | use windows_sys::Win32::Foundation::{HWND, RECT}; 14 | use windows_sys::Win32::Graphics::Gdi::HDC; 15 | use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; 16 | use windows_sys::Win32::UI::WindowsAndMessaging::GetClientRect; 17 | 18 | use crate::config::GetGlConfig; 19 | use crate::display::{DisplayFeatures, GetGlDisplay}; 20 | use crate::error::{ErrorKind, Result}; 21 | use crate::prelude::*; 22 | use crate::private::Sealed; 23 | use crate::surface::{ 24 | AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, 25 | SurfaceTypeTrait, SwapInterval, WindowSurface, 26 | }; 27 | 28 | use super::config::Config; 29 | use super::context::PossiblyCurrentContext; 30 | use super::display::Display; 31 | 32 | impl Display { 33 | pub(crate) unsafe fn create_pixmap_surface( 34 | &self, 35 | _config: &Config, 36 | _surface_attributes: &SurfaceAttributes, 37 | ) -> Result> { 38 | Err(ErrorKind::NotSupported("pixmaps are not implemented with WGL").into()) 39 | } 40 | 41 | pub(crate) unsafe fn create_pbuffer_surface( 42 | &self, 43 | config: &Config, 44 | surface_attributes: &SurfaceAttributes, 45 | ) -> Result> { 46 | let extra = self 47 | .inner 48 | .wgl_extra 49 | .filter(|_| self.inner.client_extensions.contains("WGL_ARB_pbuffer")) 50 | .ok_or(ErrorKind::NotSupported("pbuffer extensions are not supported"))?; 51 | 52 | let hdc = config.inner.hdc; 53 | let width = surface_attributes.width.unwrap().get() as c_int; 54 | let height = surface_attributes.height.unwrap().get() as c_int; 55 | let mut attrs = [0; 3]; 56 | if surface_attributes.largest_pbuffer { 57 | attrs[0] = wgl_extra::PBUFFER_LARGEST_ARB as c_int; 58 | attrs[1] = 1; 59 | } 60 | 61 | let hbuf = unsafe { 62 | extra.CreatePbufferARB( 63 | hdc as _, 64 | config.inner.pixel_format_index, 65 | width, 66 | height, 67 | attrs.as_ptr(), 68 | ) 69 | }; 70 | if hbuf.is_null() { 71 | return Err(IoError::last_os_error().into()); 72 | } 73 | 74 | let hdc = unsafe { extra.GetPbufferDCARB(hbuf) }; 75 | if hdc.is_null() { 76 | return Err(IoError::last_os_error().into()); 77 | } 78 | 79 | let surface = Surface { 80 | display: self.clone(), 81 | config: config.clone(), 82 | raw: WglSurface::PBuffer(hbuf, hdc as _), 83 | _ty: PhantomData, 84 | }; 85 | 86 | Ok(surface) 87 | } 88 | 89 | pub(crate) unsafe fn create_window_surface( 90 | &self, 91 | config: &Config, 92 | surface_attributes: &SurfaceAttributes, 93 | ) -> Result> { 94 | let hwnd = match surface_attributes.raw_window_handle.as_ref().unwrap() { 95 | handle @ RawWindowHandle::Win32(window_handle) => { 96 | let _ = unsafe { config.apply_on_native_window(handle) }; 97 | window_handle.hwnd.get() as HWND 98 | }, 99 | _ => { 100 | return Err( 101 | ErrorKind::NotSupported("provided native window is not supported").into() 102 | ); 103 | }, 104 | }; 105 | 106 | let hdc = unsafe { gdi::GetDC(hwnd) }; 107 | 108 | let surface = Surface { 109 | display: self.clone(), 110 | config: config.clone(), 111 | raw: WglSurface::Window(hwnd, hdc), 112 | _ty: PhantomData, 113 | }; 114 | 115 | Ok(surface) 116 | } 117 | } 118 | 119 | /// A Wrapper around `WglSurface`. 120 | pub struct Surface { 121 | display: Display, 122 | config: Config, 123 | pub(crate) raw: WglSurface, 124 | _ty: PhantomData, 125 | } 126 | 127 | // Impl only `Send` for Surface. 128 | unsafe impl Send for Surface {} 129 | 130 | impl Surface { 131 | fn raw_attribute(&self, attr: GLenum) -> Option { 132 | match self.raw { 133 | WglSurface::Window(..) => None, 134 | WglSurface::PBuffer(hbuf, _) => { 135 | let extra = self.display.inner.wgl_extra.unwrap(); 136 | let mut value = 0; 137 | if unsafe { extra.QueryPbufferARB(hbuf, attr as _, &mut value) } == false.into() { 138 | None 139 | } else { 140 | Some(value) 141 | } 142 | }, 143 | } 144 | } 145 | } 146 | 147 | impl Drop for Surface { 148 | fn drop(&mut self) { 149 | unsafe { 150 | match self.raw { 151 | WglSurface::Window(hwnd, hdc) => { 152 | gdi::ReleaseDC(hwnd, hdc); 153 | }, 154 | WglSurface::PBuffer(hbuf, hdc) => { 155 | let extra = self.display.inner.wgl_extra.unwrap(); 156 | extra.ReleasePbufferDCARB(hbuf, hdc as _); 157 | extra.DestroyPbufferARB(hbuf); 158 | }, 159 | } 160 | } 161 | } 162 | } 163 | 164 | impl GlSurface for Surface { 165 | type Context = PossiblyCurrentContext; 166 | type SurfaceType = T; 167 | 168 | fn buffer_age(&self) -> u32 { 169 | 0 170 | } 171 | 172 | fn width(&self) -> Option { 173 | match self.raw { 174 | WglSurface::Window(hwnd, _) => { 175 | let mut rect: RECT = unsafe { mem::zeroed() }; 176 | if unsafe { GetClientRect(hwnd, &mut rect) } == false.into() { 177 | None 178 | } else { 179 | Some((rect.right - rect.left) as u32) 180 | } 181 | }, 182 | WglSurface::PBuffer(..) => { 183 | self.raw_attribute(wgl_extra::PBUFFER_WIDTH_ARB).map(|x| x as _) 184 | }, 185 | } 186 | } 187 | 188 | fn height(&self) -> Option { 189 | match self.raw { 190 | WglSurface::Window(hwnd, _) => { 191 | let mut rect: RECT = unsafe { mem::zeroed() }; 192 | if unsafe { GetClientRect(hwnd, &mut rect) } == false.into() { 193 | None 194 | } else { 195 | Some((rect.bottom - rect.top) as u32) 196 | } 197 | }, 198 | WglSurface::PBuffer(..) => { 199 | self.raw_attribute(wgl_extra::PBUFFER_HEIGHT_ARB).map(|x| x as _) 200 | }, 201 | } 202 | } 203 | 204 | fn is_single_buffered(&self) -> bool { 205 | self.config.is_single_buffered() 206 | } 207 | 208 | fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { 209 | unsafe { 210 | if gl::SwapBuffers(self.raw.hdc()) == 0 { 211 | Err(IoError::last_os_error().into()) 212 | } else { 213 | Ok(()) 214 | } 215 | } 216 | } 217 | 218 | fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { 219 | match self.raw { 220 | WglSurface::Window(..) => { 221 | let extra = self 222 | .display 223 | .inner 224 | .wgl_extra 225 | .filter(|_| self.display.inner.features.contains(DisplayFeatures::SWAP_CONTROL)) 226 | .ok_or(ErrorKind::NotSupported("swap control extensions are not supported"))?; 227 | 228 | let interval = match interval { 229 | SwapInterval::DontWait => 0, 230 | SwapInterval::Wait(n) => n.get(), 231 | }; 232 | 233 | if unsafe { extra.SwapIntervalEXT(interval as _) } == 0 { 234 | Err(IoError::last_os_error().into()) 235 | } else { 236 | Ok(()) 237 | } 238 | }, 239 | _ => Err(ErrorKind::NotSupported("swap control not supported for surface").into()), 240 | } 241 | } 242 | 243 | fn is_current(&self, context: &Self::Context) -> bool { 244 | context.is_current() 245 | } 246 | 247 | fn is_current_draw(&self, context: &Self::Context) -> bool { 248 | context.is_current() 249 | } 250 | 251 | fn is_current_read(&self, context: &Self::Context) -> bool { 252 | context.is_current() 253 | } 254 | 255 | fn resize(&self, _context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { 256 | // This isn't supported with WGL. 257 | } 258 | } 259 | 260 | impl fmt::Debug for Surface { 261 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 262 | f.debug_struct("Surface") 263 | .field("config", &self.config.inner.pixel_format_index) 264 | .field("raw", &self.raw) 265 | .finish() 266 | } 267 | } 268 | 269 | impl AsRawSurface for Surface { 270 | fn raw_surface(&self) -> RawSurface { 271 | match self.raw { 272 | WglSurface::Window(hwnd, _) => RawSurface::Wgl(hwnd as _), 273 | WglSurface::PBuffer(hbuf, _) => RawSurface::Wgl(hbuf as _), 274 | } 275 | } 276 | } 277 | 278 | impl GetGlConfig for Surface { 279 | type Target = Config; 280 | 281 | fn config(&self) -> Self::Target { 282 | self.config.clone() 283 | } 284 | } 285 | 286 | impl GetGlDisplay for Surface { 287 | type Target = Display; 288 | 289 | fn display(&self) -> Self::Target { 290 | self.display.clone() 291 | } 292 | } 293 | 294 | impl Sealed for Surface {} 295 | 296 | /// A wrapper around WGL surfaces. 297 | #[derive(Debug)] 298 | pub(crate) enum WglSurface { 299 | /// Surface backed by a window surface. 300 | Window(HWND, HDC), 301 | /// Surface backed by a pixel buffer. 302 | PBuffer(HPBUFFEREXT, HDC), 303 | } 304 | 305 | impl WglSurface { 306 | pub(crate) fn hdc(&self) -> HDC { 307 | *match self { 308 | WglSurface::Window(_, hdc) => hdc, 309 | WglSurface::PBuffer(_, hdc) => hdc, 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /glutin/src/api/glx/surface.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to the GLXWindow. 2 | 3 | use std::fmt; 4 | use std::marker::PhantomData; 5 | use std::num::NonZeroU32; 6 | use std::os::raw::{c_int, c_uint}; 7 | 8 | use glutin_glx_sys::glx::types::GLXWindow; 9 | use glutin_glx_sys::{glx, glx_extra}; 10 | use raw_window_handle::RawWindowHandle; 11 | 12 | use crate::config::GetGlConfig; 13 | use crate::display::{DisplayFeatures, GetGlDisplay}; 14 | use crate::error::{ErrorKind, Result}; 15 | use crate::private::Sealed; 16 | use crate::surface::{ 17 | AsRawSurface, GlSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, 18 | SurfaceAttributes, SurfaceType, SurfaceTypeTrait, SwapInterval, WindowSurface, 19 | }; 20 | 21 | use super::config::Config; 22 | use super::context::PossiblyCurrentContext; 23 | use super::display::Display; 24 | 25 | /// Hint for the attributes array. 26 | const ATTR_SIZE_HINT: usize = 8; 27 | 28 | impl Display { 29 | pub(crate) unsafe fn create_pixmap_surface( 30 | &self, 31 | config: &Config, 32 | surface_attributes: &SurfaceAttributes, 33 | ) -> Result> { 34 | let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); 35 | let xid = match native_pixmap { 36 | NativePixmap::XlibPixmap(xid) => { 37 | if *xid == 0 { 38 | return Err(ErrorKind::BadNativePixmap.into()); 39 | } 40 | 41 | *xid 42 | }, 43 | _ => { 44 | return Err( 45 | ErrorKind::NotSupported("provided native pixmap is not supported.").into() 46 | ); 47 | }, 48 | }; 49 | 50 | let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); 51 | 52 | // Push X11 `None` to terminate the list. 53 | attrs.push(0); 54 | 55 | let config = config.clone(); 56 | let surface = super::last_glx_error(|| unsafe { 57 | self.inner.glx.CreatePixmap( 58 | self.inner.raw.cast(), 59 | *config.inner.raw, 60 | xid, 61 | attrs.as_ptr(), 62 | ) 63 | })?; 64 | 65 | Ok(Surface { 66 | display: self.clone(), 67 | config, 68 | raw: surface, 69 | _nosendsync: PhantomData, 70 | _ty: PhantomData, 71 | }) 72 | } 73 | 74 | pub(crate) unsafe fn create_pbuffer_surface( 75 | &self, 76 | config: &Config, 77 | surface_attributes: &SurfaceAttributes, 78 | ) -> Result> { 79 | let width = surface_attributes.width.unwrap(); 80 | let height = surface_attributes.height.unwrap(); 81 | 82 | let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); 83 | 84 | attrs.push(glx::PBUFFER_WIDTH as c_int); 85 | attrs.push(width.get() as c_int); 86 | attrs.push(glx::PBUFFER_HEIGHT as c_int); 87 | attrs.push(height.get() as c_int); 88 | attrs.push(glx::LARGEST_PBUFFER as c_int); 89 | attrs.push(surface_attributes.largest_pbuffer as c_int); 90 | 91 | // Push X11 `None` to terminate the list. 92 | attrs.push(0); 93 | 94 | let config = config.clone(); 95 | let surface = super::last_glx_error(|| unsafe { 96 | self.inner.glx.CreatePbuffer(self.inner.raw.cast(), *config.inner.raw, attrs.as_ptr()) 97 | })?; 98 | 99 | Ok(Surface { 100 | display: self.clone(), 101 | config, 102 | raw: surface, 103 | _nosendsync: PhantomData, 104 | _ty: PhantomData, 105 | }) 106 | } 107 | 108 | pub(crate) unsafe fn create_window_surface( 109 | &self, 110 | config: &Config, 111 | surface_attributes: &SurfaceAttributes, 112 | ) -> Result> { 113 | let window = match surface_attributes.raw_window_handle.unwrap() { 114 | RawWindowHandle::Xlib(window_handle) => { 115 | if window_handle.window == 0 { 116 | return Err(ErrorKind::BadNativeWindow.into()); 117 | } 118 | 119 | window_handle.window 120 | }, 121 | _ => { 122 | return Err( 123 | ErrorKind::NotSupported("provided native window is not supported").into() 124 | ); 125 | }, 126 | }; 127 | 128 | let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); 129 | 130 | // Push X11 `None` to terminate the list. 131 | attrs.push(0); 132 | 133 | let config = config.clone(); 134 | let surface = super::last_glx_error(|| unsafe { 135 | self.inner.glx.CreateWindow( 136 | self.inner.raw.cast(), 137 | *config.inner.raw, 138 | window, 139 | attrs.as_ptr() as *const _, 140 | ) 141 | })?; 142 | 143 | Ok(Surface { 144 | display: self.clone(), 145 | config, 146 | raw: surface, 147 | _nosendsync: PhantomData, 148 | _ty: PhantomData, 149 | }) 150 | } 151 | } 152 | 153 | /// A wrapper around the `GLXWindow`. 154 | pub struct Surface { 155 | display: Display, 156 | config: Config, 157 | pub(crate) raw: GLXWindow, 158 | _nosendsync: PhantomData<*const std::ffi::c_void>, 159 | _ty: PhantomData, 160 | } 161 | 162 | // Impl only `Send` for Surface. 163 | unsafe impl Send for Surface {} 164 | 165 | impl Surface { 166 | /// # Safety 167 | /// 168 | /// The caller must ensure that the attribute could be present. 169 | unsafe fn raw_attribute(&self, attr: c_int) -> c_uint { 170 | unsafe { 171 | let mut value = 0; 172 | // This shouldn't generate any errors given that we know that the surface is 173 | // valid. 174 | self.display.inner.glx.QueryDrawable( 175 | self.display.inner.raw.cast(), 176 | self.raw, 177 | attr, 178 | &mut value, 179 | ); 180 | value 181 | } 182 | } 183 | } 184 | 185 | impl Drop for Surface { 186 | fn drop(&mut self) { 187 | let _ = super::last_glx_error(|| unsafe { 188 | match T::surface_type() { 189 | SurfaceType::Pbuffer => { 190 | self.display.inner.glx.DestroyPbuffer(self.display.inner.raw.cast(), self.raw); 191 | }, 192 | SurfaceType::Window => { 193 | self.display.inner.glx.DestroyWindow(self.display.inner.raw.cast(), self.raw); 194 | }, 195 | SurfaceType::Pixmap => { 196 | self.display.inner.glx.DestroyPixmap(self.display.inner.raw.cast(), self.raw); 197 | }, 198 | } 199 | }); 200 | } 201 | } 202 | 203 | impl GlSurface for Surface { 204 | type Context = PossiblyCurrentContext; 205 | type SurfaceType = T; 206 | 207 | fn buffer_age(&self) -> u32 { 208 | if self.display.inner.client_extensions.contains("GLX_EXT_buffer_age") { 209 | unsafe { self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) as u32 } 210 | } else { 211 | 0 212 | } 213 | } 214 | 215 | fn width(&self) -> Option { 216 | unsafe { Some(self.raw_attribute(glx::WIDTH as c_int) as u32) } 217 | } 218 | 219 | fn height(&self) -> Option { 220 | unsafe { Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) } 221 | } 222 | 223 | fn is_single_buffered(&self) -> bool { 224 | self.config.is_single_buffered() 225 | } 226 | 227 | fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { 228 | super::last_glx_error(|| unsafe { 229 | self.display.inner.glx.SwapBuffers(self.display.inner.raw.cast(), self.raw); 230 | }) 231 | } 232 | 233 | fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { 234 | let extra = match self.display.inner.glx_extra { 235 | Some(extra) if self.display.inner.features.contains(DisplayFeatures::SWAP_CONTROL) => { 236 | extra 237 | }, 238 | _ => { 239 | return Err( 240 | ErrorKind::NotSupported("swap control extensions are not supported").into() 241 | ); 242 | }, 243 | }; 244 | 245 | let interval = match interval { 246 | SwapInterval::DontWait => 0, 247 | SwapInterval::Wait(n) => n.get(), 248 | }; 249 | 250 | let mut applied = false; 251 | 252 | // Apply the `EXT` first since it's per window. 253 | if !applied && self.display.inner.client_extensions.contains("GLX_EXT_swap_control") { 254 | super::last_glx_error(|| unsafe { 255 | // Check for error explicitly here, other apis do have indication for failure. 256 | extra.SwapIntervalEXT(self.display.inner.raw.cast(), self.raw, interval as _); 257 | applied = true; 258 | })?; 259 | } 260 | 261 | if !applied && self.display.inner.client_extensions.contains("GLX_MESA_swap_control") { 262 | unsafe { 263 | applied = extra.SwapIntervalMESA(interval as _) != glx::BAD_CONTEXT as _; 264 | } 265 | } 266 | 267 | if !applied && self.display.inner.client_extensions.contains("GLX_SGI_swap_control") { 268 | unsafe { 269 | applied = extra.SwapIntervalSGI(interval as _) != glx::BAD_CONTEXT as _; 270 | } 271 | } 272 | 273 | if applied { Ok(()) } else { Err(ErrorKind::BadContext.into()) } 274 | } 275 | 276 | fn is_current(&self, context: &Self::Context) -> bool { 277 | self.is_current_draw(context) && self.is_current_read(context) 278 | } 279 | 280 | fn is_current_draw(&self, _context: &Self::Context) -> bool { 281 | unsafe { self.display.inner.glx.GetCurrentDrawable() == self.raw } 282 | } 283 | 284 | fn is_current_read(&self, _context: &Self::Context) -> bool { 285 | unsafe { self.display.inner.glx.GetCurrentReadDrawable() == self.raw } 286 | } 287 | 288 | fn resize(&self, _context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { 289 | // This isn't supported with GLXDrawable. 290 | } 291 | } 292 | 293 | impl GetGlConfig for Surface { 294 | type Target = Config; 295 | 296 | fn config(&self) -> Self::Target { 297 | self.config.clone() 298 | } 299 | } 300 | 301 | impl GetGlDisplay for Surface { 302 | type Target = Display; 303 | 304 | fn display(&self) -> Self::Target { 305 | self.display.clone() 306 | } 307 | } 308 | 309 | impl fmt::Debug for Surface { 310 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 311 | f.debug_struct("Surface") 312 | .field("display", &self.display.inner.raw) 313 | .field("config", &self.config.inner.raw) 314 | .field("raw", &self.raw) 315 | .field("type", &T::surface_type()) 316 | .finish() 317 | } 318 | } 319 | 320 | impl AsRawSurface for Surface { 321 | fn raw_surface(&self) -> RawSurface { 322 | RawSurface::Glx(self.raw as u64) 323 | } 324 | } 325 | 326 | impl Sealed for Surface {} 327 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Kirill Chibisov 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /glutin_examples/examples/switch_render_thread.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::num::NonZeroU32; 3 | use std::sync::mpsc::{self, Sender}; 4 | use std::sync::{Arc, Mutex}; 5 | use std::thread; 6 | 7 | use glutin::config::ConfigTemplateBuilder; 8 | use glutin::context::{ContextAttributesBuilder, PossiblyCurrentContext}; 9 | use glutin::display::GetGlDisplay; 10 | use glutin::prelude::*; 11 | use glutin::surface::{Surface, WindowSurface}; 12 | use glutin_examples::gl::types::GLfloat; 13 | use glutin_examples::{Renderer, gl_config_picker}; 14 | use glutin_winit::{self, DisplayBuilder, GlWindow}; 15 | use raw_window_handle::HasWindowHandle; 16 | use winit::application::ApplicationHandler; 17 | use winit::dpi::PhysicalSize; 18 | use winit::event::{ElementState, WindowEvent}; 19 | use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}; 20 | use winit::window::Window; 21 | 22 | fn main() -> Result<(), Box> { 23 | let event_loop = EventLoop::::with_user_event().build().unwrap(); 24 | 25 | // `EventLoopProxy` allows you to dispatch custom events to the main Winit event 26 | // loop from any thread. 27 | let event_loop_proxy = event_loop.create_proxy(); 28 | 29 | let mut app = App::new(event_loop_proxy); 30 | 31 | event_loop.run_app(&mut app)?; 32 | 33 | app.exit_state 34 | } 35 | 36 | struct App { 37 | event_loop_proxy: EventLoopProxy, 38 | exit_state: Result<(), Box>, 39 | state: Option, 40 | } 41 | 42 | impl App { 43 | fn new(event_loop_proxy: EventLoopProxy) -> Self { 44 | Self { event_loop_proxy, exit_state: Ok(()), state: None } 45 | } 46 | } 47 | 48 | impl ApplicationHandler for App { 49 | fn resumed(&mut self, event_loop: &ActiveEventLoop) { 50 | if self.state.is_some() { 51 | return; 52 | } 53 | 54 | let (window, render_context) = match create_window_with_render_context(event_loop) { 55 | Ok(ok) => ok, 56 | Err(e) => { 57 | self.exit_state = Err(e); 58 | event_loop.exit(); 59 | return; 60 | }, 61 | }; 62 | let render_context = Arc::new(Mutex::new(render_context)); 63 | 64 | let (_render_threads, render_thread_senders) = 65 | spawn_render_threads(render_context, self.event_loop_proxy.clone()); 66 | 67 | let state = AppState { 68 | _window: window, 69 | render_thread_senders, 70 | render_thread_index: 0, 71 | thread_switch_in_progress: false, 72 | }; 73 | state.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); 74 | assert!(self.state.replace(state).is_none()); 75 | } 76 | 77 | fn window_event( 78 | &mut self, 79 | event_loop: &ActiveEventLoop, 80 | _window_id: winit::window::WindowId, 81 | event: WindowEvent, 82 | ) { 83 | match event { 84 | WindowEvent::CloseRequested => event_loop.exit(), 85 | WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => { 86 | self.state.as_ref().unwrap().send_event_to_current_render_thread( 87 | RenderThreadEvent::Resize(PhysicalSize { 88 | width: NonZeroU32::new(size.width).unwrap(), 89 | height: NonZeroU32::new(size.height).unwrap(), 90 | }), 91 | ); 92 | }, 93 | WindowEvent::RedrawRequested => { 94 | self.state 95 | .as_ref() 96 | .unwrap() 97 | .send_event_to_current_render_thread(RenderThreadEvent::Draw); 98 | }, 99 | WindowEvent::MouseInput { state: ElementState::Pressed, .. } => { 100 | self.state.as_mut().unwrap().start_render_thread_switch(); 101 | }, 102 | _ => (), 103 | } 104 | } 105 | 106 | fn user_event(&mut self, _event_loop: &ActiveEventLoop, _event: PlatformThreadEvent) { 107 | self.state.as_mut().unwrap().complete_render_thread_switch(); 108 | } 109 | } 110 | 111 | struct AppState { 112 | render_thread_senders: Vec>, 113 | render_thread_index: usize, 114 | thread_switch_in_progress: bool, 115 | _window: Window, 116 | } 117 | 118 | impl AppState { 119 | fn send_event_to_current_render_thread(&self, event: RenderThreadEvent) { 120 | if self.thread_switch_in_progress { 121 | return; 122 | } 123 | 124 | if let Some(tx) = self.render_thread_senders.get(self.render_thread_index) { 125 | tx.send(event).expect("sending event to current render thread failed"); 126 | } 127 | } 128 | 129 | fn start_render_thread_switch(&mut self) { 130 | self.send_event_to_current_render_thread(RenderThreadEvent::MakeNotCurrent); 131 | 132 | self.thread_switch_in_progress = true; 133 | } 134 | 135 | fn complete_render_thread_switch(&mut self) { 136 | self.thread_switch_in_progress = false; 137 | 138 | self.render_thread_index += 1; 139 | if self.render_thread_index == self.render_thread_senders.len() { 140 | self.render_thread_index = 0; 141 | } 142 | 143 | self.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); 144 | self.send_event_to_current_render_thread(RenderThreadEvent::Draw); 145 | } 146 | } 147 | 148 | /// A rendering context that can be shared between tasks. 149 | struct RenderContext { 150 | context: PossiblyCurrentContext, 151 | surface: Surface, 152 | renderer: Renderer, 153 | } 154 | 155 | unsafe impl Send for RenderContext {} 156 | 157 | impl RenderContext { 158 | fn new( 159 | context: PossiblyCurrentContext, 160 | surface: Surface, 161 | renderer: Renderer, 162 | ) -> Self { 163 | Self { context, surface, renderer } 164 | } 165 | 166 | fn make_current(&mut self) -> Result<(), impl Error> { 167 | self.context.make_current(&self.surface) 168 | } 169 | 170 | fn make_not_current(&mut self) -> Result<(), impl Error> { 171 | self.context.make_not_current_in_place() 172 | } 173 | 174 | fn swap_buffers(&mut self) -> Result<(), impl Error> { 175 | self.surface.swap_buffers(&self.context) 176 | } 177 | 178 | fn draw_with_clear_color(&self, red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) { 179 | self.renderer.draw_with_clear_color(red, green, blue, alpha) 180 | } 181 | 182 | fn resize(&mut self, size: PhysicalSize) { 183 | self.surface.resize(&self.context, size.width, size.height); 184 | self.renderer.resize(size.width.get() as i32, size.height.get() as i32); 185 | } 186 | } 187 | 188 | fn create_window_with_render_context( 189 | event_loop: &ActiveEventLoop, 190 | ) -> Result<(Window, RenderContext), Box> { 191 | let window_attributes = Window::default_attributes().with_transparent(true); 192 | 193 | let template = ConfigTemplateBuilder::new().with_alpha_size(8); 194 | 195 | let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes)); 196 | 197 | let (mut window, gl_config) = display_builder.build(event_loop, template, gl_config_picker)?; 198 | 199 | println!("Picked a config with {} samples", gl_config.num_samples()); 200 | 201 | let raw_window_handle = window 202 | .as_ref() 203 | .and_then(|window| window.window_handle().map(|handle| handle.as_raw()).ok()); 204 | 205 | let window = window.take().unwrap(); 206 | 207 | let gl_display = gl_config.display(); 208 | 209 | let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle); 210 | 211 | let not_current_gl_context = unsafe { 212 | gl_display 213 | .create_context(&gl_config, &context_attributes) 214 | .expect("failed to create context") 215 | }; 216 | 217 | let attrs = window 218 | .build_surface_attributes(<_>::default()) 219 | .expect("Failed to build surface attributes"); 220 | let gl_surface = 221 | unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() }; 222 | 223 | // Make it current. 224 | let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap(); 225 | 226 | // The context needs to be current for the Renderer to set up shaders and 227 | // buffers. It also performs function loading, which needs a current context on 228 | // WGL. 229 | let renderer = Renderer::new(&gl_display); 230 | 231 | let gl_context = gl_context.make_not_current().unwrap().treat_as_possibly_current(); 232 | 233 | Ok((window, RenderContext::new(gl_context, gl_surface, renderer))) 234 | } 235 | 236 | fn spawn_render_threads( 237 | render_context: Arc>, 238 | event_loop_proxy: EventLoopProxy, 239 | ) -> (Vec, Vec>) { 240 | let mut senders = Vec::new(); 241 | let mut render_threads = Vec::new(); 242 | 243 | for id in 0..3 { 244 | let render_thread = RenderThread::new(id, render_context.clone()); 245 | let tx = render_thread.spawn(event_loop_proxy.clone()); 246 | 247 | render_threads.push(render_thread); 248 | senders.push(tx); 249 | } 250 | 251 | (render_threads, senders) 252 | } 253 | 254 | #[derive(Debug, Clone, Copy, Default)] 255 | struct Color { 256 | r: GLfloat, 257 | g: GLfloat, 258 | b: GLfloat, 259 | a: GLfloat, 260 | } 261 | 262 | impl Color { 263 | fn new(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat) -> Self { 264 | Self { r, g, b, a } 265 | } 266 | 267 | fn new_from_index(color_index: i32) -> Self { 268 | match color_index { 269 | 0 => Color::new(1.0, 0.0, 0.0, 0.9), 270 | 1 => Color::new(0.0, 1.0, 0.0, 0.9), 271 | 2 => Color::new(0.0, 0.0, 1.0, 0.9), 272 | _ => Default::default(), 273 | } 274 | } 275 | } 276 | 277 | #[derive(Debug, Clone, Copy)] 278 | enum RenderThreadEvent { 279 | Draw, 280 | MakeCurrent, 281 | MakeNotCurrent, 282 | Resize(PhysicalSize), 283 | } 284 | 285 | #[derive(Debug, Clone, Copy)] 286 | enum PlatformThreadEvent { 287 | ContextNotCurrent, 288 | } 289 | 290 | struct RenderThread { 291 | id: i32, 292 | color: Color, 293 | render_context: Arc>, 294 | } 295 | 296 | impl RenderThread { 297 | fn new(id: i32, render_context: Arc>) -> Self { 298 | let color = Color::new_from_index(id); 299 | Self { id, color, render_context } 300 | } 301 | 302 | fn spawn( 303 | &self, 304 | event_loop_proxy: EventLoopProxy, 305 | ) -> Sender { 306 | let (tx, rx) = mpsc::channel(); 307 | 308 | let (id, color, render_context) = (self.id, self.color, self.render_context.clone()); 309 | 310 | thread::spawn(move || { 311 | for event in rx { 312 | let mut render_context_guard = render_context.lock().unwrap(); 313 | 314 | match event { 315 | RenderThreadEvent::Draw => { 316 | println!("thread {}: drawing", id); 317 | render_context_guard 318 | .draw_with_clear_color(color.r, color.g, color.b, color.a); 319 | render_context_guard.swap_buffers().expect("swap buffers failed"); 320 | }, 321 | RenderThreadEvent::MakeCurrent => { 322 | println!("thread {}: make current", id); 323 | render_context_guard.make_current().expect("make current failed"); 324 | }, 325 | RenderThreadEvent::MakeNotCurrent => { 326 | println!("thread {}: make not current", id); 327 | render_context_guard.make_not_current().expect("make not current failed"); 328 | event_loop_proxy 329 | .send_event(PlatformThreadEvent::ContextNotCurrent) 330 | .expect("sending context-not-current event failed"); 331 | }, 332 | RenderThreadEvent::Resize(size) => { 333 | render_context_guard.resize(size); 334 | }, 335 | } 336 | } 337 | }); 338 | 339 | tx 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /glutin/src/api/glx/config.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to finding and manipulating the `GLXFBConfig`. 2 | 3 | use std::ops::Deref; 4 | use std::os::raw::c_int; 5 | use std::sync::Arc; 6 | use std::{fmt, slice}; 7 | 8 | use glutin_glx_sys::glx::types::GLXFBConfig; 9 | use glutin_glx_sys::{glx, glx_extra}; 10 | use raw_window_handle::RawWindowHandle; 11 | 12 | use crate::config::{ 13 | Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, 14 | }; 15 | use crate::display::{DisplayFeatures, GetGlDisplay}; 16 | use crate::error::{ErrorKind, Result}; 17 | use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB}; 18 | use crate::private::Sealed; 19 | 20 | use super::display::Display; 21 | 22 | impl Display { 23 | pub(crate) unsafe fn find_configs( 24 | &self, 25 | template: ConfigTemplate, 26 | ) -> Result + '_>> { 27 | let mut config_attributes = Vec::::new(); 28 | 29 | // Add color buffer type. 30 | match template.color_buffer_type { 31 | ColorBufferType::Rgb { r_size, g_size, b_size } => { 32 | // Type. 33 | config_attributes.push(glx::X_VISUAL_TYPE as c_int); 34 | config_attributes.push(glx::TRUE_COLOR as c_int); 35 | 36 | // R. 37 | config_attributes.push(glx::RED_SIZE as c_int); 38 | config_attributes.push(r_size as c_int); 39 | 40 | // G. 41 | config_attributes.push(glx::GREEN_SIZE as c_int); 42 | config_attributes.push(g_size as c_int); 43 | 44 | // B. 45 | config_attributes.push(glx::BLUE_SIZE as c_int); 46 | config_attributes.push(b_size as c_int); 47 | }, 48 | ColorBufferType::Luminance(luminance) => { 49 | // Type. 50 | config_attributes.push(glx::X_VISUAL_TYPE as c_int); 51 | config_attributes.push(glx::GRAY_SCALE as c_int); 52 | 53 | // L. 54 | config_attributes.push(glx::RED_SIZE as c_int); 55 | config_attributes.push(luminance as c_int); 56 | }, 57 | }; 58 | 59 | // Render type. 60 | config_attributes.push(glx::RENDER_TYPE as c_int); 61 | 62 | if template.float_pixels 63 | && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) 64 | { 65 | config_attributes.push(glx_extra::RGBA_FLOAT_BIT_ARB as c_int); 66 | } else if template.float_pixels { 67 | return Err(ErrorKind::NotSupported("float pixels are not supported").into()); 68 | } else { 69 | config_attributes.push(glx::RGBA_BIT as c_int); 70 | } 71 | 72 | // Add caveat. 73 | if let Some(hardware_accelerated) = template.hardware_accelerated { 74 | config_attributes.push(glx::CONFIG_CAVEAT as c_int); 75 | if hardware_accelerated { 76 | config_attributes.push(glx::NONE as c_int); 77 | } else { 78 | config_attributes.push(glx::SLOW_CONFIG as c_int); 79 | } 80 | } 81 | 82 | // Double buffer. 83 | config_attributes.push(glx::DOUBLEBUFFER as c_int); 84 | config_attributes.push(!template.single_buffering as c_int); 85 | 86 | // Add alpha. 87 | config_attributes.push(glx::ALPHA_SIZE as c_int); 88 | config_attributes.push(template.alpha_size as c_int); 89 | 90 | // Add depth. 91 | config_attributes.push(glx::DEPTH_SIZE as c_int); 92 | config_attributes.push(template.depth_size as c_int); 93 | 94 | // Add stencil. 95 | config_attributes.push(glx::STENCIL_SIZE as c_int); 96 | config_attributes.push(template.stencil_size as c_int); 97 | 98 | // Add visual if was provided. 99 | if let Some(RawWindowHandle::Xlib(window)) = template.native_window { 100 | if window.visual_id > 0 { 101 | config_attributes.push(glx::VISUAL_ID as c_int); 102 | config_attributes.push(window.visual_id as c_int); 103 | } 104 | } 105 | 106 | // Add surface type. 107 | config_attributes.push(glx::DRAWABLE_TYPE as c_int); 108 | let mut surface_type = 0; 109 | if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { 110 | surface_type |= glx::WINDOW_BIT; 111 | } 112 | if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { 113 | surface_type |= glx::PBUFFER_BIT; 114 | } 115 | if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { 116 | surface_type |= glx::PIXMAP_BIT; 117 | } 118 | config_attributes.push(surface_type as c_int); 119 | 120 | // Add maximum height of pbuffer. 121 | if let Some(pbuffer_width) = template.max_pbuffer_width { 122 | config_attributes.push(glx::MAX_PBUFFER_WIDTH as c_int); 123 | config_attributes.push(pbuffer_width as c_int); 124 | } 125 | 126 | // Add maximum width of pbuffer. 127 | if let Some(pbuffer_height) = template.max_pbuffer_height { 128 | config_attributes.push(glx::MAX_PBUFFER_HEIGHT as c_int); 129 | config_attributes.push(pbuffer_height as c_int); 130 | } 131 | 132 | // Add stereoscopy, if present. 133 | if let Some(stereoscopy) = template.stereoscopy { 134 | config_attributes.push(glx::STEREO as c_int); 135 | config_attributes.push(stereoscopy as c_int); 136 | } 137 | 138 | // Add multisampling. 139 | if let Some(num_samples) = template.num_samples { 140 | if self.inner.features.contains(DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS) { 141 | config_attributes.push(glx::SAMPLE_BUFFERS as c_int); 142 | config_attributes.push(1); 143 | config_attributes.push(glx::SAMPLES as c_int); 144 | config_attributes.push(num_samples as c_int); 145 | } 146 | } 147 | 148 | // Push X11 `None` to terminate the list. 149 | config_attributes.push(0); 150 | 151 | unsafe { 152 | let mut num_configs = 0; 153 | let raw_configs = self.inner.glx.ChooseFBConfig( 154 | self.inner.raw.cast(), 155 | self.inner.screen as _, 156 | config_attributes.as_ptr() as *const _, 157 | &mut num_configs, 158 | ); 159 | 160 | if raw_configs.is_null() { 161 | return Err(ErrorKind::BadConfig.into()); 162 | } 163 | 164 | let configs = slice::from_raw_parts_mut(raw_configs, num_configs as usize).to_vec(); 165 | 166 | // Free the memory from the Xlib, since we've just copied it. 167 | (XLIB.as_ref().unwrap().XFree)(raw_configs as *mut _); 168 | 169 | let iter = configs 170 | .into_iter() 171 | .map(move |raw| { 172 | let raw = GlxConfig(raw); 173 | let inner = Arc::new(ConfigInner { display: self.clone(), raw }); 174 | Config { inner } 175 | }) 176 | .filter(move |config| { 177 | !template.transparency || config.supports_transparency().unwrap_or(false) 178 | }); 179 | 180 | Ok(Box::new(iter)) 181 | } 182 | } 183 | } 184 | 185 | /// A wrapper around `GLXFBConfig`. 186 | #[derive(Debug, Clone, PartialEq, Eq)] 187 | pub struct Config { 188 | pub(crate) inner: Arc, 189 | } 190 | 191 | impl Config { 192 | /// # Safety 193 | /// 194 | /// The caller must ensure that the attribute could be present. 195 | unsafe fn raw_attribute(&self, attr: c_int) -> c_int { 196 | unsafe { 197 | let mut val = 0; 198 | self.inner.display.inner.glx.GetFBConfigAttrib( 199 | self.inner.display.inner.raw.cast(), 200 | *self.inner.raw, 201 | attr, 202 | &mut val, 203 | ); 204 | val as c_int 205 | } 206 | } 207 | 208 | pub(crate) fn is_single_buffered(&self) -> bool { 209 | unsafe { self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 } 210 | } 211 | } 212 | 213 | impl GlConfig for Config { 214 | fn color_buffer_type(&self) -> Option { 215 | unsafe { 216 | match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ { 217 | glx::TRUE_COLOR => { 218 | let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8; 219 | let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8; 220 | let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8; 221 | Some(ColorBufferType::Rgb { r_size, g_size, b_size }) 222 | }, 223 | glx::GRAY_SCALE => { 224 | let luma = self.raw_attribute(glx::RED_SIZE as c_int); 225 | Some(ColorBufferType::Luminance(luma as u8)) 226 | }, 227 | _ => None, 228 | } 229 | } 230 | } 231 | 232 | fn float_pixels(&self) -> bool { 233 | if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) { 234 | let render_type = 235 | unsafe { self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum }; 236 | render_type == glx_extra::RGBA_FLOAT_BIT_ARB 237 | } else { 238 | false 239 | } 240 | } 241 | 242 | fn alpha_size(&self) -> u8 { 243 | unsafe { self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 } 244 | } 245 | 246 | fn hardware_accelerated(&self) -> bool { 247 | unsafe { self.raw_attribute(glx::CONFIG_CAVEAT as c_int) != glx::SLOW_CONFIG as c_int } 248 | } 249 | 250 | fn srgb_capable(&self) -> bool { 251 | if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { 252 | unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } 253 | } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { 254 | unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } 255 | } else { 256 | false 257 | } 258 | } 259 | 260 | fn depth_size(&self) -> u8 { 261 | unsafe { self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 } 262 | } 263 | 264 | fn stencil_size(&self) -> u8 { 265 | unsafe { self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 } 266 | } 267 | 268 | fn num_samples(&self) -> u8 { 269 | unsafe { self.raw_attribute(glx::SAMPLES as c_int) as u8 } 270 | } 271 | 272 | fn config_surface_types(&self) -> ConfigSurfaceTypes { 273 | let mut ty = ConfigSurfaceTypes::empty(); 274 | 275 | let raw_ty = unsafe { self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32 }; 276 | if raw_ty & glx::WINDOW_BIT as u32 != 0 { 277 | ty.insert(ConfigSurfaceTypes::WINDOW); 278 | } 279 | if raw_ty & glx::PBUFFER_BIT as u32 != 0 { 280 | ty.insert(ConfigSurfaceTypes::PBUFFER); 281 | } 282 | if raw_ty & glx::PIXMAP_BIT as u32 != 0 { 283 | ty.insert(ConfigSurfaceTypes::PIXMAP); 284 | } 285 | 286 | ty 287 | } 288 | 289 | fn supports_transparency(&self) -> Option { 290 | self.x11_visual().map(|visual| visual.supports_transparency()) 291 | } 292 | 293 | fn api(&self) -> Api { 294 | let mut api = Api::OPENGL; 295 | if self.inner.display.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT) { 296 | api |= Api::GLES1 | Api::GLES2; 297 | } 298 | 299 | api 300 | } 301 | } 302 | 303 | impl X11GlConfigExt for Config { 304 | fn x11_visual(&self) -> Option { 305 | unsafe { 306 | let raw_visual = self 307 | .inner 308 | .display 309 | .inner 310 | .glx 311 | .GetVisualFromFBConfig(self.inner.display.inner.raw.cast(), *self.inner.raw); 312 | if raw_visual.is_null() { 313 | None 314 | } else { 315 | Some(X11VisualInfo::from_raw( 316 | self.inner.display.inner.raw.cast(), 317 | raw_visual as *mut _, 318 | )) 319 | } 320 | } 321 | } 322 | } 323 | 324 | impl GetGlDisplay for Config { 325 | type Target = Display; 326 | 327 | fn display(&self) -> Self::Target { 328 | self.inner.display.clone() 329 | } 330 | } 331 | 332 | impl AsRawConfig for Config { 333 | fn raw_config(&self) -> RawConfig { 334 | RawConfig::Glx(*self.inner.raw) 335 | } 336 | } 337 | 338 | impl Sealed for Config {} 339 | 340 | pub(crate) struct ConfigInner { 341 | display: Display, 342 | pub(crate) raw: GlxConfig, 343 | } 344 | 345 | impl PartialEq for ConfigInner { 346 | fn eq(&self, other: &Self) -> bool { 347 | self.raw == other.raw 348 | } 349 | } 350 | 351 | impl Eq for ConfigInner {} 352 | 353 | impl fmt::Debug for ConfigInner { 354 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 355 | f.debug_struct("Config") 356 | .field("raw", &self.raw) 357 | .field("display", &self.display.inner.raw) 358 | .finish() 359 | } 360 | } 361 | 362 | #[derive(Debug, Clone, PartialEq, Eq)] 363 | pub(crate) struct GlxConfig(GLXFBConfig); 364 | 365 | unsafe impl Send for GlxConfig {} 366 | unsafe impl Sync for GlxConfig {} 367 | 368 | impl Deref for GlxConfig { 369 | type Target = GLXFBConfig; 370 | 371 | fn deref(&self) -> &Self::Target { 372 | &self.0 373 | } 374 | } 375 | --------------------------------------------------------------------------------