glutin-0.32.2/.cargo_vcs_info.json0000644000000001440000000000100124210ustar { "git": { "sha1": "d9b4fd7036386c5f23b0a1015a31a986805d4db3" }, "path_in_vcs": "glutin" }glutin-0.32.2/Cargo.toml0000644000000075350000000000100104320ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70.0" name = "glutin" version = "0.32.2" authors = ["Kirill Chibisov "] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Cross-platform OpenGL context provider." documentation = "https://docs.rs/glutin" readme = "README.md" keywords = [ "windowing", "opengl", "egl", ] license = "Apache-2.0" repository = "https://github.com/rust-windowing/glutin" [package.metadata.docs.rs] default-target = "x86_64-unknown-linux-gnu" rustdoc-args = [ "--cfg", "docsrs", ] targets = [ "aarch64-linux-android", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-apple-darwin", "i686-unknown-linux-gnu", "i686-pc-windows-msvc", ] [lib] name = "glutin" path = "src/lib.rs" [dependencies.bitflags] version = "2.2.1" [dependencies.libloading] version = "0.8.0" optional = true [dependencies.once_cell] version = "1.13" [dependencies.raw-window-handle] version = "0.6.2" [build-dependencies.cfg_aliases] version = "0.2.1" [features] default = [ "egl", "glx", "x11", "wayland", "wgl", ] egl = [ "glutin_egl_sys", "libloading", ] glx = [ "x11", "glutin_glx_sys", "libloading", ] wayland = [ "wayland-sys", "egl", ] wgl = [ "glutin_wgl_sys", "windows-sys", ] x11 = ["x11-dl"] [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies.glutin_egl_sys] version = "0.7.1" optional = true [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies.glutin_glx_sys] version = "0.6.1" optional = true [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies.wayland-sys] version = "0.31.1" features = [ "egl", "client", "dlopen", ] optional = true default-features = false [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies.x11-dl] version = "2.20.0" optional = true [target.'cfg(any(target_os = "macos"))'.dependencies.cgl] version = "0.3.2" [target.'cfg(any(target_os = "macos"))'.dependencies.core-foundation] version = "0.9.3" [target.'cfg(any(target_os = "macos"))'.dependencies.dispatch] version = "0.2.0" [target.'cfg(any(target_os = "macos"))'.dependencies.objc2] version = "0.5.1" [target.'cfg(any(target_os = "macos"))'.dependencies.objc2-app-kit] version = "0.2.0" features = [ "NSApplication", "NSResponder", "NSView", "NSWindow", "NSOpenGL", "NSOpenGLView", ] [target.'cfg(any(target_os = "macos"))'.dependencies.objc2-foundation] version = "0.2.0" features = [ "dispatch", "NSArray", "NSThread", ] [target.'cfg(target_os = "android")'.dependencies.glutin_egl_sys] version = "0.7.1" [target."cfg(windows)".dependencies.glutin_egl_sys] version = "0.7.1" optional = true [target."cfg(windows)".dependencies.glutin_wgl_sys] version = "0.6.1" optional = true [target."cfg(windows)".dependencies.windows-sys] version = "0.52" features = [ "Win32_Foundation", "Win32_Graphics_Gdi", "Win32_Graphics_OpenGL", "Win32_System_LibraryLoader", "Win32_UI_WindowsAndMessaging", ] optional = true glutin-0.32.2/Cargo.toml.orig000064400000000000000000000051161046102023000141040ustar 00000000000000[package] name = "glutin" version = "0.32.2" authors = ["Kirill Chibisov "] description = "Cross-platform OpenGL context provider." keywords = ["windowing", "opengl", "egl"] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/rust-windowing/glutin" documentation = "https://docs.rs/glutin" rust-version = "1.70.0" edition = "2021" [features] default = ["egl", "glx", "x11", "wayland", "wgl"] egl = ["glutin_egl_sys", "libloading"] glx = ["x11", "glutin_glx_sys", "libloading"] wgl = ["glutin_wgl_sys", "windows-sys"] x11 = ["x11-dl"] wayland = ["wayland-sys", "egl"] [dependencies] bitflags = "2.2.1" libloading = { version = "0.8.0", optional = true } once_cell = "1.13" raw-window-handle = "0.6.2" [target.'cfg(windows)'.dependencies] glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys", optional = true } glutin_wgl_sys = { version = "0.6.1", path = "../glutin_wgl_sys", optional = true } [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52" features = [ "Win32_Foundation", "Win32_Graphics_Gdi", "Win32_Graphics_OpenGL", "Win32_System_LibraryLoader", "Win32_UI_WindowsAndMessaging", ] optional = true [target.'cfg(target_os = "android")'.dependencies] glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys" } [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] glutin_egl_sys = { version = "0.7.1", path = "../glutin_egl_sys", optional = true } glutin_glx_sys = { version = "0.6.1", path = "../glutin_glx_sys", optional = true } wayland-sys = { version = "0.31.1", default-features = false, features = ["egl", "client", "dlopen"], optional = true } x11-dl = { version = "2.20.0", optional = true } [target.'cfg(any(target_os = "macos"))'.dependencies] cgl = "0.3.2" core-foundation = "0.9.3" objc2 = "0.5.1" dispatch = "0.2.0" [target.'cfg(any(target_os = "macos"))'.dependencies.objc2-foundation] version = "0.2.0" features = [ "dispatch", "NSArray", "NSThread", ] [target.'cfg(any(target_os = "macos"))'.dependencies.objc2-app-kit] version = "0.2.0" features = [ "NSApplication", "NSResponder", "NSView", "NSWindow", "NSOpenGL", "NSOpenGLView", ] [build-dependencies] cfg_aliases = "0.2.1" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] targets = [ "aarch64-linux-android", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-apple-darwin", "i686-unknown-linux-gnu", "i686-pc-windows-msvc", ] default-target = "x86_64-unknown-linux-gnu" glutin-0.32.2/LICENSE000064400000000000000000000260601046102023000122230ustar 00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Kirill Chibisov Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. glutin-0.32.2/README.md000064400000000000000000000037411046102023000124760ustar 00000000000000# glutin - OpenGL, UTilities, and INput A low-level library for OpenGL context creation. [![](https://img.shields.io/crates/v/glutin.svg)](https://crates.io/crates/glutin) [![Docs.rs](https://docs.rs/glutin/badge.svg)](https://docs.rs/glutin) ## [Documentation](https://docs.rs/glutin) ## Contact Us Join us in any of these: [![Matrix](https://img.shields.io/badge/Matrix-%23winit%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#winit:matrix.org) [![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit) ## Usage Examples **Warning:** These are examples for `master`. You can find examples for the latest _released version_ [here](https://github.com/rust-windowing/glutin/releases/latest). The examples use [`gl_generator`](https://crates.io/crates/gl_generator) to generate OpenGL bindings. ### Try it! ```bash git clone https://github.com/rust-windowing/glutin cd glutin cargo run --example window ``` ### Usage Glutin is an OpenGL context creation library, and doesn't directly provide OpenGL bindings for you. For examples, please look [here](https://github.com/rust-windowing/glutin/tree/master/glutin_examples). Note that glutin aims at being a low-level brick in your rendering infrastructure. You are encouraged to write another layer of abstraction between glutin and your application. Glutin follows winit's [MSRV policy](https://github.com/rust-windowing/winit/blob/master/README.md#msrv-policy). ## Platform-specific notes ### Android Be sure to handle Android's lifecycle correctly when using a `winit` window by only creating a GL surface after `winit` raises `Event::Resumed`, and destroy it again upon receiving `Event::Suspended`. See this in action in the [`android.rs` example](./glutin_examples/examples/android.rs). To compile and run the Android example on your device, install [`cargo-apk`](https://crates.io/crates/cargo-apk) and start the app using: ```console $ cargo apk r -p glutin_examples --example android ``` glutin-0.32.2/build.rs000064400000000000000000000020521046102023000126560ustar 00000000000000use cfg_aliases::cfg_aliases; fn main() { // Setup alias to reduce `cfg` boilerplate. cfg_aliases! { // Systems. android_platform: { target_os = "android" }, ohos_platform: { target_env = "ohos" }, wasm_platform: { target_family = "wasm" }, macos_platform: { target_os = "macos" }, ios_platform: { target_os = "ios" }, apple: { any(ios_platform, macos_platform) }, free_unix: { all(unix, not(apple), not(android_platform), not(ohos_platform)) }, // Native displays. x11_platform: { all(feature = "x11", free_unix, not(wasm_platform)) }, wayland_platform: { all(feature = "wayland", free_unix, not(wasm_platform)) }, // Backends. egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm_platform)) }, glx_backend: { all(feature = "glx", x11_platform, not(wasm_platform)) }, wgl_backend: { all(feature = "wgl", windows, not(wasm_platform)) }, cgl_backend: { all(macos_platform, not(wasm_platform)) }, } } glutin-0.32.2/src/api/cgl/appkit.rs000064400000000000000000000067411046102023000151650ustar 00000000000000//! Parts of AppKit related to OpenGL that is not yet in `objc2-app-kit`. #![allow(dead_code)] #![allow(non_snake_case)] use objc2::encode::{Encoding, RefEncode}; use objc2::rc::{Allocated, Id}; use objc2::{extern_class, extern_methods, mutability, ClassType}; #[allow(deprecated)] use objc2_app_kit::{NSOpenGLContextParameter, NSOpenGLPixelFormatAttribute, NSView}; use objc2_foundation::{MainThreadMarker, NSObject}; pub type GLint = i32; #[repr(C)] pub struct CGLContextObj { __inner: [u8; 0], } unsafe impl RefEncode for CGLContextObj { const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("_CGLContextObject", &[])); } extern_class!( #[derive(Debug, PartialEq, Eq, Hash)] pub(crate) struct NSOpenGLContext; // Strict order required by macro, tracked in https://github.com/madsmtm/objc2/issues/479 #[rustfmt::skip] unsafe impl ClassType for NSOpenGLContext { type Super = NSObject; type Mutability = mutability::InteriorMutable; } ); unsafe impl Send for NSOpenGLContext {} unsafe impl Sync for NSOpenGLContext {} extern_methods!( unsafe impl NSOpenGLContext { #[method_id(currentContext)] pub(crate) fn currentContext() -> Option>; #[method_id(initWithFormat:shareContext:)] pub(crate) fn initWithFormat_shareContext( this: Allocated, format: &NSOpenGLPixelFormat, share: Option<&NSOpenGLContext>, ) -> Option>; #[method(clearCurrentContext)] pub(crate) fn clearCurrentContext(); #[method(makeCurrentContext)] pub(crate) fn makeCurrentContext(&self); #[method(update)] pub(crate) fn update(&self); #[method(flushBuffer)] pub(crate) fn flushBuffer(&self); #[method_id(view)] pub(crate) fn view(&self, mtm: MainThreadMarker) -> Option>; #[method(setView:)] pub(crate) unsafe fn setView(&self, view: Option<&NSView>); #[allow(deprecated)] #[method(setValues:forParameter:)] pub(crate) unsafe fn setValues_forParameter( &self, vals: *const GLint, param: NSOpenGLContextParameter, ); #[method(CGLContextObj)] pub(crate) fn CGLContextObj(&self) -> *mut CGLContextObj; } ); extern_class!( #[derive(Debug, PartialEq, Eq, Hash)] pub(crate) struct NSOpenGLPixelFormat; // Strict order required by macro, tracked in https://github.com/madsmtm/objc2/issues/479 #[rustfmt::skip] unsafe impl ClassType for NSOpenGLPixelFormat { type Super = NSObject; type Mutability = mutability::Immutable; } ); unsafe impl Send for NSOpenGLPixelFormat {} unsafe impl Sync for NSOpenGLPixelFormat {} extern_methods!( unsafe impl NSOpenGLPixelFormat { #[method_id(initWithAttributes:)] unsafe fn initWithAttributes( this: Allocated, attrs: *const NSOpenGLPixelFormatAttribute, ) -> Option>; pub(crate) unsafe fn newWithAttributes( attrs: &[NSOpenGLPixelFormatAttribute], ) -> Option> { unsafe { Self::initWithAttributes(Self::alloc(), attrs.as_ptr()) } } #[method(getValues:forAttribute:forVirtualScreen:)] pub(crate) unsafe fn getValues_forAttribute_forVirtualScreen( &self, vals: *mut GLint, param: NSOpenGLPixelFormatAttribute, screen: GLint, ); } ); glutin-0.32.2/src/api/cgl/config.rs000064400000000000000000000161621046102023000151400ustar 00000000000000//! Everything related to `NSOpenGLPixelFormat`. use std::sync::Arc; use std::{fmt, iter}; use objc2::rc::Id; #[allow(deprecated)] use objc2_app_kit::{ NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, NSOpenGLPFAAlphaSize, NSOpenGLPFAColorFloat, NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAMinimumPolicy, NSOpenGLPFAMultisample, NSOpenGLPFAOpenGLProfile, NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize, NSOpenGLPFAStereo, NSOpenGLPFATripleBuffer, NSOpenGLPixelFormatAttribute, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersionLegacy, }; use crate::config::{ Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, }; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::private::Sealed; use super::appkit::NSOpenGLPixelFormat; use super::display::Display; impl Display { #[allow(deprecated)] pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { let mut attrs = Vec::::with_capacity(32); // We use minimum to follow behavior of other platforms here. attrs.push(NSOpenGLPFAMinimumPolicy); // Allow offline renderers. attrs.push(NSOpenGLPFAAllowOfflineRenderers); // Color. match template.color_buffer_type { ColorBufferType::Rgb { r_size, g_size, b_size } => { attrs.push(NSOpenGLPFAColorSize); // We can't specify particular color, so we provide the sum, and also requires // an alpha. attrs.push((r_size + g_size + b_size + template.alpha_size) as u32); }, _ => { return Err( ErrorKind::NotSupported("luminance buffers are not supported with CGL").into() ) }, } // Alpha. attrs.push(NSOpenGLPFAAlphaSize); attrs.push(template.alpha_size as u32); // Depth. attrs.push(NSOpenGLPFADepthSize); attrs.push(template.depth_size as u32); // Stencil. attrs.push(NSOpenGLPFAStencilSize); attrs.push(template.stencil_size as u32); // Float colors. if template.float_pixels { attrs.push(NSOpenGLPFAColorFloat); } // Sample buffers. if let Some(num_samples) = template.num_samples { attrs.push(NSOpenGLPFAMultisample); attrs.push(NSOpenGLPFASampleBuffers); attrs.push(1); attrs.push(NSOpenGLPFASamples); attrs.push(num_samples as u32); } // Double buffering. if !template.single_buffering { attrs.push(NSOpenGLPFADoubleBuffer); } if template.hardware_accelerated == Some(true) { attrs.push(NSOpenGLPFAAccelerated); } // Stereo. if template.stereoscopy == Some(true) { attrs.push(NSOpenGLPFAStereo); } attrs.push(NSOpenGLPFAOpenGLProfile); // Stash profile pos for latter insert. let profile_attr_pos = attrs.len(); // Add place holder for the GL profile. attrs.push(NSOpenGLProfileVersion4_1Core); // Terminate attrs with zero. attrs.push(0); // Automatically pick the latest profile. let raw = [ NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersionLegacy, ] .into_iter() .find_map(|profile| { attrs[profile_attr_pos] = profile; // initWithAttributes returns None if the attributes were invalid unsafe { NSOpenGLPixelFormat::newWithAttributes(&attrs) } }) .ok_or(ErrorKind::BadConfig)?; let inner = Arc::new(ConfigInner { display: self.clone(), raw, transparency: template.transparency, }); let config = Config { inner }; Ok(Box::new(iter::once(config))) } } /// A wrapper around NSOpenGLPixelFormat. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { pub(crate) inner: Arc, } impl Config { fn raw_attribute(&self, attrib: NSOpenGLPixelFormatAttribute) -> i32 { unsafe { let mut value = 0; self.inner.raw.getValues_forAttribute_forVirtualScreen( &mut value, attrib, // They do differ per monitor and require context. Which is kind of insane, but // whatever. Zero is a primary monitor. 0, ); value } } #[allow(deprecated)] pub(crate) fn is_single_buffered(&self) -> bool { self.raw_attribute(NSOpenGLPFATripleBuffer) == 0 && self.raw_attribute(NSOpenGLPFADoubleBuffer) == 0 } } #[allow(deprecated)] impl GlConfig for Config { fn color_buffer_type(&self) -> Option { // On macos all color formats divide by 3 without reminder, except for the RGB // 565. So we can convert it in a hopefully reliable way. Also we should remove // alpha. let color = self.raw_attribute(NSOpenGLPFAColorSize) - self.alpha_size() as i32; let r_size = (color / 3) as u8; let b_size = (color / 3) as u8; let g_size = (color - r_size as i32 - b_size as i32) as u8; Some(ColorBufferType::Rgb { r_size, g_size, b_size }) } fn float_pixels(&self) -> bool { self.raw_attribute(NSOpenGLPFAColorFloat) != 0 } fn alpha_size(&self) -> u8 { self.raw_attribute(NSOpenGLPFAAlphaSize) as u8 } fn srgb_capable(&self) -> bool { true } fn hardware_accelerated(&self) -> bool { self.raw_attribute(NSOpenGLPFAAccelerated) != 0 } fn depth_size(&self) -> u8 { self.raw_attribute(NSOpenGLPFADepthSize) as u8 } fn stencil_size(&self) -> u8 { self.raw_attribute(NSOpenGLPFAStencilSize) as u8 } fn num_samples(&self) -> u8 { self.raw_attribute(NSOpenGLPFASamples) as u8 } fn config_surface_types(&self) -> ConfigSurfaceTypes { ConfigSurfaceTypes::WINDOW } fn supports_transparency(&self) -> Option { Some(self.inner.transparency) } fn api(&self) -> Api { Api::OPENGL } } impl GetGlDisplay for Config { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawConfig for Config { fn raw_config(&self) -> RawConfig { RawConfig::Cgl(Id::as_ptr(&self.inner.raw).cast()) } } impl Sealed for Config {} pub(crate) struct ConfigInner { display: Display, pub(crate) transparency: bool, pub(crate) raw: Id, } impl PartialEq for ConfigInner { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } impl Eq for ConfigInner {} impl fmt::Debug for ConfigInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Config").field("id", &self.raw).finish() } } glutin-0.32.2/src/api/cgl/context.rs000064400000000000000000000213561046102023000153600ustar 00000000000000//! Everything related to `NSOpenGLContext`. use std::fmt; use std::marker::PhantomData; use cgl::CGLSetParameter; use objc2::rc::{autoreleasepool, Id}; use objc2::ClassType; use objc2_app_kit::{NSOpenGLCPSwapInterval, NSView}; use objc2_foundation::{run_on_main, MainThreadBound}; use crate::config::GetGlConfig; use crate::context::{ AsRawContext, ContextApi, ContextAttributes, Priority, RawContext, Robustness, }; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{SurfaceTypeTrait, SwapInterval}; use super::appkit::NSOpenGLContext; use super::config::Config; use super::display::Display; use super::surface::Surface; impl Display { pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, ) -> Result { let share_context = match context_attributes.shared_context.as_ref() { Some(RawContext::Cgl(share_context)) => unsafe { share_context.cast::().as_ref() }, _ => None, }; if matches!(context_attributes.api, Some(ContextApi::Gles(_))) { return Err(ErrorKind::NotSupported("gles is not supported with CGL").into()); } if context_attributes.robustness != Robustness::NotRobust { return Err(ErrorKind::NotSupported("robustness is not supported with CGL").into()); } let config = config.clone(); let raw = NSOpenGLContext::initWithFormat_shareContext( NSOpenGLContext::alloc(), &config.inner.raw, share_context, ) .ok_or(ErrorKind::BadConfig)?; if config.inner.transparency { let opacity = 0; super::check_error(unsafe { CGLSetParameter(raw.CGLContextObj().cast(), cgl::kCGLCPSurfaceOpacity, &opacity) })?; } let inner = ContextInner { display: self.clone(), config, raw }; let context = NotCurrentContext::new(inner); Ok(context) } } /// A wrapper arounh `NSOpenGLContext` that is known to be not current on the /// current thread. #[derive(Debug)] pub struct NotCurrentContext { pub(crate) inner: ContextInner, _nosync: PhantomData>, } impl NotCurrentContext { /// Make a [`Self::PossiblyCurrentContext`] indicating that the context /// could be current on the thread. pub fn make_current_surfaceless(self) -> Result { self.inner.make_current_surfaceless()?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn new(inner: ContextInner) -> Self { Self { inner, _nosync: PhantomData } } } impl NotCurrentGlContext for NotCurrentContext { type PossiblyCurrentContext = PossiblyCurrentContext; type Surface = Surface; fn treat_as_possibly_current(self) -> PossiblyCurrentContext { PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } } fn make_current( self, surface: &Self::Surface, ) -> Result { self.inner.make_current(surface)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn make_current_draw_read( self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result { Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) } } impl GlContext for NotCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl GetGlConfig for NotCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for NotCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for NotCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Cgl(Id::as_ptr(&self.inner.raw).cast()) } } impl Sealed for NotCurrentContext {} /// A wrapper around `NSOpenGLContext` that could be curront on the current /// thread. #[derive(Debug)] pub struct PossiblyCurrentContext { pub(crate) inner: ContextInner, // The context could be current only on the one thread. _nosendsync: PhantomData<*mut ()>, } impl PossiblyCurrentContext { /// Make this context current on the calling thread. pub fn make_current_surfaceless(&self) -> Result<()> { self.inner.make_current_surfaceless() } } impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; type Surface = Surface; fn make_not_current(self) -> Result { self.make_not_current_in_place()?; Ok(NotCurrentContext::new(self.inner)) } fn make_not_current_in_place(&self) -> Result<()> { self.inner.make_not_current() } fn is_current(&self) -> bool { if let Some(current) = NSOpenGLContext::currentContext() { current == self.inner.raw } else { false } } fn make_current(&self, surface: &Self::Surface) -> Result<()> { self.inner.make_current(surface) } fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()> { Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) } } impl GlContext for PossiblyCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl GetGlConfig for PossiblyCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for PossiblyCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for PossiblyCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Cgl(Id::as_ptr(&self.inner.raw).cast()) } } impl Sealed for PossiblyCurrentContext {} pub(crate) struct ContextInner { display: Display, config: Config, pub(crate) raw: Id, } impl ContextInner { fn make_current_draw_read( &self, _surface_draw: &Surface, _surface_read: &Surface, ) -> ErrorKind { ErrorKind::NotSupported("make current draw read isn't supported with CGL") } fn make_current(&self, surface: &Surface) -> Result<()> { autoreleasepool(|_| { self.update(); self.raw.makeCurrentContext(); let view = &surface.ns_view; run_on_main(|mtm| unsafe { self.raw.setView(Some(view.get(mtm))); }); Ok(()) }) } fn make_current_surfaceless(&self) -> Result<()> { autoreleasepool(|_| { self.update(); self.raw.makeCurrentContext(); run_on_main(|_mtm| unsafe { self.raw.setView(None); }); Ok(()) }) } fn context_api(&self) -> ContextApi { ContextApi::OpenGl(None) } pub(crate) fn set_swap_interval(&self, interval: SwapInterval) { let interval = match interval { SwapInterval::DontWait => 0, SwapInterval::Wait(_) => 1, }; autoreleasepool(|_| unsafe { self.raw.setValues_forParameter(&interval, NSOpenGLCPSwapInterval); }) } pub(crate) fn update(&self) { run_on_main(|_| self.raw.update()); } pub(crate) fn flush_buffer(&self) -> Result<()> { autoreleasepool(|_| { self.raw.flushBuffer(); Ok(()) }) } pub(crate) fn is_view_current(&self, view: &MainThreadBound>) -> bool { run_on_main(|mtm| { self.raw.view(mtm).expect("context to have a current view") == *view.get(mtm) }) } fn make_not_current(&self) -> Result<()> { self.update(); NSOpenGLContext::clearCurrentContext(); Ok(()) } } impl fmt::Debug for ContextInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") .field("config", &self.config.inner.raw) .field("raw", &self.raw) .finish() } } glutin-0.32.2/src/api/cgl/display.rs000064400000000000000000000071361046102023000153410ustar 00000000000000//! A CGL display. use std::ffi::{self, CStr}; use std::marker::PhantomData; use core_foundation::base::TCFType; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use core_foundation::string::CFString; use raw_window_handle::RawDisplayHandle; use crate::config::ConfigTemplate; use crate::display::{AsRawDisplay, DisplayFeatures, RawDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; use super::config::Config; use super::context::NotCurrentContext; use super::surface::Surface; /// The CGL display. #[derive(Debug, Clone)] pub struct Display { // Prevent building of it without constructor. _marker: PhantomData<()>, } impl Display { /// Create CGL display. /// /// # Safety /// /// The function is unsafe for consistency. pub unsafe fn new(display: RawDisplayHandle) -> Result { match display { RawDisplayHandle::AppKit(..) => Ok(Display { _marker: PhantomData }), _ => Err(ErrorKind::NotSupported("provided native display is not supported").into()), } } } impl GlDisplay for Display { type Config = Config; type NotCurrentContext = NotCurrentContext; type PbufferSurface = Surface; type PixmapSurface = Surface; type WindowSurface = Surface; unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( &self, config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { let symbol_name = CFString::new(addr.to_str().unwrap()); let framework_name = CFString::new("com.apple.opengl"); unsafe { let framework = CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()); CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()).cast() } } fn version_string(&self) -> String { String::from("Apple CGL") } fn supported_features(&self) -> DisplayFeatures { DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS | DisplayFeatures::FLOAT_PIXEL_FORMAT | DisplayFeatures::SRGB_FRAMEBUFFERS | DisplayFeatures::SWAP_CONTROL } } impl AsRawDisplay for Display { fn raw_display(&self) -> RawDisplay { RawDisplay::Cgl } } impl Sealed for Display {} glutin-0.32.2/src/api/cgl/mod.rs000064400000000000000000000042261046102023000144500ustar 00000000000000//! The CGL Api. #![allow(non_upper_case_globals)] #![allow(clippy::let_unit_value)] // Temporary use std::ffi::CStr; use std::os::raw::c_int; use cgl::{kCGLNoError, CGLError, CGLErrorString}; use crate::error::{Error, ErrorKind, Result}; mod appkit; pub mod config; pub mod context; pub mod display; pub mod surface; const kCGLBadAttribute: c_int = 10000; const kCGLBadProperty: c_int = 10001; const kCGLBadPixelFormat: c_int = 10002; const kCGLBadRendererInfo: c_int = 10003; const kCGLBadContext: c_int = 10004; const kCGLBadDrawable: c_int = 10005; const kCGLBadDisplay: c_int = 10006; const kCGLBadState: c_int = 10007; const kCGLBadValue: c_int = 10008; const kCGLBadMatch: c_int = 10009; const kCGLBadEnumeration: c_int = 10010; const kCGLBadOffScreen: c_int = 10011; const kCGLBadFullScreen: c_int = 10012; const kCGLBadWindow: c_int = 10013; const kCGLBadAddress: c_int = 10014; const kCGLBadCodeModule: c_int = 10015; const kCGLBadAlloc: c_int = 10016; const kCGLBadConnection: c_int = 10017; pub(crate) fn check_error(error: CGLError) -> Result<()> { let kind = match error { kCGLNoError => return Ok(()), kCGLBadAttribute => ErrorKind::BadAttribute, kCGLBadProperty => ErrorKind::BadParameter, kCGLBadPixelFormat => ErrorKind::BadConfig, kCGLBadContext => ErrorKind::BadContext, kCGLBadDrawable => ErrorKind::BadSurface, kCGLBadDisplay => ErrorKind::BadDisplay, kCGLBadState => ErrorKind::BadContextState, kCGLBadValue => ErrorKind::BadAttribute, kCGLBadEnumeration => ErrorKind::BadAttribute, kCGLBadOffScreen => ErrorKind::BadSurface, kCGLBadMatch => ErrorKind::BadMatch, kCGLBadWindow => ErrorKind::BadNativeWindow, kCGLBadAddress => ErrorKind::BadAccess, kCGLBadAlloc => ErrorKind::OutOfMemory, kCGLBadCodeModule | kCGLBadConnection | kCGLBadRendererInfo | kCGLBadFullScreen => { ErrorKind::Misc }, _ => ErrorKind::Misc, }; let description = unsafe { CStr::from_ptr(CGLErrorString(error)).to_str().unwrap_or_default().to_string() }; Err(Error::new(Some(error as _), Some(description), kind)) } glutin-0.32.2/src/api/cgl/surface.rs000064400000000000000000000153731046102023000153260ustar 00000000000000//! Wrapper around `NSView`. use std::fmt; use std::marker::PhantomData; use std::num::NonZeroU32; use objc2::rc::Id; use objc2_app_kit::{NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSView}; use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker}; use raw_window_handle::RawWindowHandle; use crate::config::GetGlConfig; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::private::Sealed; use crate::surface::{ AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, SurfaceTypeTrait, SwapInterval, WindowSurface, }; use super::config::Config; use super::context::PossiblyCurrentContext; use super::display::Display; impl Display { pub(crate) unsafe fn create_pixmap_surface( &self, _config: &Config, _surface_attributes: &SurfaceAttributes, ) -> Result> { Err(ErrorKind::NotSupported("pixmaps are not supported with CGL").into()) } pub(crate) unsafe fn create_pbuffer_surface( &self, _config: &Config, _surface_attributes: &SurfaceAttributes, ) -> Result> { Err(ErrorKind::NotSupported("pbuffers are not supported with CGL").into()) } pub(crate) unsafe fn create_window_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let native_window = match surface_attributes.raw_window_handle.unwrap() { RawWindowHandle::AppKit(window) => window, _ => { return Err( ErrorKind::NotSupported("provided native window is not supported").into() ) }, }; // SAFETY: The objects below must have been created on the main thread // in the first place, so we can safely "move" them back to that thread. let mtm = unsafe { MainThreadMarker::new_unchecked() }; // SAFETY: Validity of the view and window is ensured by caller // This function makes sure the window is non null. let ns_view = if let Some(ns_view) = unsafe { Id::retain(native_window.ns_view.as_ptr().cast::()) } { ns_view } else { return Err(ErrorKind::NotSupported("ns_view of provided native window is nil").into()); }; // The default value of `wantsBestResolutionOpenGLSurface` is `false` when // linked with the macOS 10.14 SDK and `true` if linked with a macOS 10.15 SDK // or newer. We always set it to `true` because we want High DPI surfaces, and // we want to avoid this confusing default system value. #[allow(deprecated)] ns_view.setWantsBestResolutionOpenGLSurface(true); // On Mojave, views apparently automatically become layer-backed shortly after // being added to a window. Changing the layer-backedness of a view breaks the // association between the view and its associated OpenGL context. To work // around this, we explicitly make the view layer-backed up front so that AppKit // doesn't do it itself and break the association with its context. if unsafe { NSAppKitVersionNumber }.floor() > NSAppKitVersionNumber10_12 { ns_view.setWantsLayer(true); } let ns_view = MainThreadBound::new(ns_view, mtm); let surface = Surface { display: self.clone(), config: config.clone(), ns_view, _nosync: PhantomData, _ty: PhantomData, }; Ok(surface) } } /// A wrapper around `NSView`. pub struct Surface { display: Display, config: Config, pub(crate) ns_view: MainThreadBound>, _nosync: PhantomData<*const std::ffi::c_void>, _ty: PhantomData, } // Impl only `Send` for Surface. unsafe impl Send for Surface {} impl GlSurface for Surface { type Context = PossiblyCurrentContext; type SurfaceType = T; fn buffer_age(&self) -> u32 { 0 } fn width(&self) -> Option { let view = &self.ns_view; run_on_main(|mtm| { let view = view.get(mtm); let scale_factor = match view.window() { Some(window) => window.backingScaleFactor(), None => 1.0, }; let frame = view.frame(); Some((frame.size.width * scale_factor) as u32) }) } fn height(&self) -> Option { let view = &self.ns_view; run_on_main(|mtm| { let view = view.get(mtm); let scale_factor = match view.window() { Some(window) => window.backingScaleFactor(), None => 1.0, }; let frame = view.frame(); Some((frame.size.height * scale_factor) as u32) }) } fn is_single_buffered(&self) -> bool { self.config.is_single_buffered() } fn swap_buffers(&self, context: &Self::Context) -> Result<()> { context.inner.flush_buffer() } fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> { context.inner.set_swap_interval(interval); Ok(()) } fn is_current(&self, context: &Self::Context) -> bool { context.inner.is_view_current(&self.ns_view) } fn is_current_draw(&self, context: &Self::Context) -> bool { self.is_current(context) } fn is_current_read(&self, context: &Self::Context) -> bool { self.is_current(context) } fn resize(&self, context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { context.inner.update(); } } impl GetGlConfig for Surface { type Target = Config; fn config(&self) -> Self::Target { self.config.clone() } } impl GetGlDisplay for Surface { type Target = Display; fn display(&self) -> Self::Target { self.display.clone() } } impl AsRawSurface for Surface { fn raw_surface(&self) -> RawSurface { // SAFETY: We only use the thread marker to get the pointer value of the view let mtm = unsafe { MainThreadMarker::new_unchecked() }; RawSurface::Cgl(Id::as_ptr(self.ns_view.get(mtm)).cast()) } } impl fmt::Debug for Surface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Surface") .field("config", &self.config.inner.raw) .field("ns_view", &self.ns_view) .field("type", &T::surface_type()) .finish() } } impl Sealed for Surface {} glutin-0.32.2/src/api/egl/config.rs000064400000000000000000000340611046102023000151400ustar 00000000000000//! Everything related to finding and manipulating the `EGLConfig`. #![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support use std::ops::Deref; use std::sync::Arc; use std::{fmt, mem}; use raw_window_handle::RawWindowHandle; use glutin_egl_sys::egl; use glutin_egl_sys::egl::types::{EGLConfig, EGLint}; use crate::config::{ Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; #[cfg(x11_platform)] use crate::platform::x11::{X11GlConfigExt, X11VisualInfo}; use super::display::Display; impl Display { pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { let mut config_attributes = Vec::::new(); // Add color buffer type. match template.color_buffer_type { ColorBufferType::Rgb { r_size, g_size, b_size } => { // Type. config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint); config_attributes.push(egl::RGB_BUFFER as EGLint); // R. config_attributes.push(egl::RED_SIZE as EGLint); config_attributes.push(r_size as EGLint); // G. config_attributes.push(egl::GREEN_SIZE as EGLint); config_attributes.push(g_size as EGLint); // B. config_attributes.push(egl::BLUE_SIZE as EGLint); config_attributes.push(b_size as EGLint); }, ColorBufferType::Luminance(luminance) => { // Type. config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint); config_attributes.push(egl::LUMINANCE_BUFFER as EGLint); // L. config_attributes.push(egl::LUMINANCE_SIZE as EGLint); config_attributes.push(luminance as EGLint); }, }; if template.float_pixels && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) { config_attributes.push(egl::COLOR_COMPONENT_TYPE_EXT as EGLint); config_attributes.push(egl::COLOR_COMPONENT_TYPE_FLOAT_EXT as EGLint); } else if template.float_pixels { return Err(ErrorKind::NotSupported("float pixels not supported").into()); } // Add alpha. config_attributes.push(egl::ALPHA_SIZE as EGLint); config_attributes.push(template.alpha_size as EGLint); // Add depth. config_attributes.push(egl::DEPTH_SIZE as EGLint); config_attributes.push(template.depth_size as EGLint); // Add stencil. config_attributes.push(egl::STENCIL_SIZE as EGLint); config_attributes.push(template.stencil_size as EGLint); // Add surface type. config_attributes.push(egl::SURFACE_TYPE as EGLint); let mut surface_type = 0; if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { surface_type |= egl::WINDOW_BIT; } if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { surface_type |= egl::PBUFFER_BIT; } if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { surface_type |= egl::PIXMAP_BIT; } config_attributes.push(surface_type as EGLint); // Add caveat. if let Some(hardware_accelerated) = template.hardware_accelerated { config_attributes.push(egl::CONFIG_CAVEAT as EGLint); if hardware_accelerated { config_attributes.push(egl::NONE as EGLint); } else { config_attributes.push(egl::SLOW_CONFIG as EGLint); } } // Add minimum swap interval. if let Some(min_swap_interval) = template.min_swap_interval { config_attributes.push(egl::MIN_SWAP_INTERVAL as EGLint); config_attributes.push(min_swap_interval as EGLint) } // Add maximum swap interval. if let Some(max_swap_interval) = template.max_swap_interval { config_attributes.push(egl::MAX_SWAP_INTERVAL as EGLint); config_attributes.push(max_swap_interval as EGLint) } // Add multisampling. if let Some(num_samples) = template.num_samples { config_attributes.push(egl::SAMPLE_BUFFERS as EGLint); config_attributes.push(1); config_attributes.push(egl::SAMPLES as EGLint); config_attributes.push(num_samples as EGLint); } config_attributes.push(egl::RENDERABLE_TYPE as EGLint); let api = if let Some(requested_api) = template.api { let mut api = 0; if requested_api.contains(Api::GLES1) { api |= egl::OPENGL_ES_BIT; } if requested_api.contains(Api::GLES2) { api |= egl::OPENGL_ES2_BIT; } if requested_api.contains(Api::GLES3) { api |= egl::OPENGL_ES3_BIT; } if requested_api.contains(Api::OPENGL) { api |= egl::OPENGL_BIT; } api } else { // NOTE: use ES2 by default to avoid matching pure ES1 configs, // for more see https://github.com/rust-windowing/glutin/issues/1586. egl::OPENGL_ES2_BIT }; config_attributes.push(api as EGLint); // Add maximum height of pbuffer. if let Some(pbuffer_width) = template.max_pbuffer_width { config_attributes.push(egl::MAX_PBUFFER_WIDTH as EGLint); config_attributes.push(pbuffer_width as EGLint); } // Add maximum width of pbuffer. if let Some(pbuffer_height) = template.max_pbuffer_height { config_attributes.push(egl::MAX_PBUFFER_HEIGHT as EGLint); config_attributes.push(pbuffer_height as EGLint); } // Push `egl::NONE` to terminate the list. config_attributes.push(egl::NONE as EGLint); let mut configs_number = self.configs_number() as EGLint; let mut found_configs: Vec = unsafe { vec![mem::zeroed(); configs_number as usize] }; unsafe { let result = self.inner.egl.ChooseConfig( *self.inner.raw, config_attributes.as_ptr(), found_configs.as_mut_ptr(), configs_number as EGLint, &mut configs_number, ); if result == egl::FALSE { return Err(ErrorKind::BadConfig.into()); } found_configs.set_len(configs_number as usize); } let configs = found_configs .into_iter() .map(move |raw| { let raw = EglConfig(raw); let inner = Arc::new(ConfigInner { display: self.clone(), raw }); Config { inner } }) .filter(move |config| { // Filter configs not compatible with the native window. // // XXX This can't be done by passing visual in the EGL attributes // when calling `eglChooseConfig` since the visual is ignored. match template.native_window { Some(RawWindowHandle::Xcb(xcb)) => { xcb.visual_id.map_or(false, |id| id.get() == config.native_visual()) }, Some(RawWindowHandle::Xlib(xlib)) if xlib.visual_id > 0 => { xlib.visual_id as u32 == config.native_visual() }, _ => true, } }) .filter(move |config| { !template.transparency || config.supports_transparency().unwrap_or(true) }); Ok(Box::new(configs)) } fn configs_number(&self) -> usize { unsafe { let mut num_configs = 0; self.inner.egl.GetConfigs(*self.inner.raw, std::ptr::null_mut(), 0, &mut num_configs); num_configs as usize } } } /// A simple wrapper around `EGLConfig` that could be used with `EGLContext` /// and `EGLSurface`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { pub(crate) inner: Arc, } impl Config { /// The native visual identifier. /// /// The interpretation of this value is platform dependant. Consult /// `platform` extension you're ended up using. pub fn native_visual(&self) -> u32 { unsafe { self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 } } /// # Safety /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint { unsafe { let mut val = 0; self.inner.display.inner.egl.GetConfigAttrib( *self.inner.display.inner.raw, *self.inner.raw, attr, &mut val, ); val as EGLint } } } impl GlConfig for Config { fn color_buffer_type(&self) -> Option { unsafe { match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ { egl::LUMINANCE_BUFFER => { let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint); Some(ColorBufferType::Luminance(luma as u8)) }, egl::RGB_BUFFER => { let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8; let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8; let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8; Some(ColorBufferType::Rgb { r_size, g_size, b_size }) }, _ => None, } } } fn float_pixels(&self) -> bool { unsafe { if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) { matches!( self.raw_attribute(egl::COLOR_COMPONENT_TYPE_EXT as EGLint) as _, egl::COLOR_COMPONENT_TYPE_FLOAT_EXT ) } else { false } } } fn alpha_size(&self) -> u8 { unsafe { self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 } } fn srgb_capable(&self) -> bool { self.inner.display.inner.features.contains(DisplayFeatures::SRGB_FRAMEBUFFERS) } fn depth_size(&self) -> u8 { unsafe { self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 } } fn stencil_size(&self) -> u8 { unsafe { self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 } } fn num_samples(&self) -> u8 { unsafe { self.raw_attribute(egl::SAMPLES as EGLint) as u8 } } fn config_surface_types(&self) -> ConfigSurfaceTypes { let mut ty = ConfigSurfaceTypes::empty(); let raw_ty = unsafe { self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32 }; if raw_ty & egl::WINDOW_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::WINDOW); } if raw_ty & egl::PBUFFER_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::PBUFFER); } if raw_ty & egl::PIXMAP_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::PIXMAP); } ty } fn hardware_accelerated(&self) -> bool { unsafe { self.raw_attribute(egl::CONFIG_CAVEAT as EGLint) != egl::SLOW_CONFIG as EGLint } } fn supports_transparency(&self) -> Option { match *self.inner.display.inner._native_display? { #[cfg(x11_platform)] raw_window_handle::RawDisplayHandle::Xlib(_) | raw_window_handle::RawDisplayHandle::Xcb(_) => { self.x11_visual().map(|visual| visual.supports_transparency()) }, #[cfg(wayland_platform)] raw_window_handle::RawDisplayHandle::Wayland(_) => Some(self.alpha_size() != 0), _ => None, } } fn api(&self) -> Api { let mut api = Api::empty(); let raw_api = unsafe { self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32 }; if raw_api & egl::OPENGL_BIT as u32 != 0 { api.insert(Api::OPENGL); } if raw_api & egl::OPENGL_ES_BIT as u32 != 0 { api.insert(Api::GLES1); } if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 { api.insert(Api::GLES2); } if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 { api.insert(Api::GLES3); } api } } impl GetGlDisplay for Config { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawConfig for Config { fn raw_config(&self) -> RawConfig { RawConfig::Egl(*self.inner.raw) } } #[cfg(x11_platform)] impl X11GlConfigExt for Config { fn x11_visual(&self) -> Option { match *self.inner.display.inner._native_display? { raw_window_handle::RawDisplayHandle::Xlib(display_handle) => unsafe { let xid = self.native_visual(); X11VisualInfo::from_xid(display_handle.display?.as_ptr() as *mut _, xid as _) }, _ => None, } } } impl Sealed for Config {} pub(crate) struct ConfigInner { display: Display, pub(crate) raw: EglConfig, } impl PartialEq for ConfigInner { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } impl Eq for ConfigInner {} impl fmt::Debug for ConfigInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Config") .field("raw", &self.raw) .field("display", &self.display.inner.raw) .finish() } } #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct EglConfig(EGLConfig); unsafe impl Send for EglConfig {} unsafe impl Sync for EglConfig {} impl Deref for EglConfig { type Target = EGLConfig; fn deref(&self) -> &Self::Target { &self.0 } } glutin-0.32.2/src/api/egl/context.rs000064400000000000000000000377461046102023000153740ustar 00000000000000//! Everything related to `EGLContext` management. use std::fmt; use std::marker::PhantomData; use std::ops::Deref; use glutin_egl_sys::egl::types::{EGLenum, EGLint}; use glutin_egl_sys::{egl, EGLContext}; use crate::config::{Api, GetGlConfig}; use crate::context::{ self, AsRawContext, ContextApi, ContextAttributes, GlProfile, Priority, RawContext, Robustness, Version, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::SurfaceTypeTrait; use super::config::Config; use super::display::Display; use super::surface::Surface; impl Display { pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, ) -> Result { let mut attrs = Vec::::new(); let supports_opengl = self.inner.version > Version::new(1, 3); let config_api = config.api(); let (api, mut version) = match context_attributes.api { api @ Some(ContextApi::OpenGl(_)) | api @ None if supports_opengl && config_api.contains(Api::OPENGL) => { (egl::OPENGL_API, api.and_then(|api| api.version())) }, api @ Some(ContextApi::Gles(_)) | api @ None => { let version = match api.and_then(|api| api.version()) { Some(version) => version, None if config_api.contains(Api::GLES3) => Version::new(3, 0), None if config_api.contains(Api::GLES2) => Version::new(2, 0), _ => Version::new(1, 0), }; (egl::OPENGL_ES_API, Some(version)) }, _ => { return Err( ErrorKind::NotSupported("the requested context Api isn't supported.").into() ) }, }; let is_one_five = self.inner.version >= Version::new(1, 5); if is_one_five || self.inner.display_extensions.contains("EGL_KHR_create_context") { let mut flags = 0; // Add profile for the OpenGL Api. if api == egl::OPENGL_API { let (profile, new_version) = context::pick_profile(context_attributes.profile, version); version = Some(new_version); let profile = match profile { GlProfile::Core => egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, GlProfile::Compatibility => egl::CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, }; attrs.push(egl::CONTEXT_OPENGL_PROFILE_MASK as EGLint); attrs.push(profile as EGLint); } if let Some(version) = version { attrs.push(egl::CONTEXT_MAJOR_VERSION as EGLint); attrs.push(version.major as EGLint); attrs.push(egl::CONTEXT_MINOR_VERSION as EGLint); attrs.push(version.minor as EGLint); } let has_robustsess = self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS); let mut requested_no_error = false; match context_attributes.robustness { Robustness::NotRobust => (), Robustness::NoError if self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) => { attrs.push(egl::CONTEXT_OPENGL_NO_ERROR_KHR as EGLint); attrs.push(egl::TRUE as EGLint); requested_no_error = true; }, Robustness::RobustLoseContextOnReset if has_robustsess => { attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); attrs.push(egl::LOSE_CONTEXT_ON_RESET as EGLint); flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; }, Robustness::RobustNoResetNotification if has_robustsess => { attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); attrs.push(egl::NO_RESET_NOTIFICATION as EGLint); flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; }, _ => { return Err( ErrorKind::NotSupported("context robustness is not supported").into() ) }, } if context_attributes.debug && is_one_five && !requested_no_error { attrs.push(egl::CONTEXT_OPENGL_DEBUG as EGLint); attrs.push(egl::TRUE as EGLint); } if flags != 0 { attrs.push(egl::CONTEXT_FLAGS_KHR as EGLint); attrs.push(flags as EGLint); } } else if self.inner.version >= Version::new(1, 3) { // EGL 1.3 uses that to indicate client version instead of major/minor. The // constant is the same as `CONTEXT_MAJOR_VERSION`. if let Some(version) = version { attrs.push(egl::CONTEXT_CLIENT_VERSION as EGLint); attrs.push(version.major as EGLint); } } if let Some(priority) = context_attributes.priority.filter(|_| { let extensions = &self.inner.display_extensions; // Some android versions don't report support for this extension, even though // it's supported. // // https://github.com/googlevr/gvr-android-sdk/issues/330 #[cfg(android_platform)] let android = extensions.contains("EGL_ANDROID_front_buffer_auto_refresh") && extensions.contains("EGL_ANDROID_create_native_client_buffer"); #[cfg(not(android_platform))] let android = false; extensions.contains("EGL_IMG_context_priority") || android }) { let priority = match priority { Priority::Low => egl::CONTEXT_PRIORITY_LOW_IMG, Priority::Medium => egl::CONTEXT_PRIORITY_MEDIUM_IMG, Priority::High => egl::CONTEXT_PRIORITY_HIGH_IMG, Priority::Realtime => { if self.inner.display_extensions.contains("EGL_NV_context_priority_realtime") { egl::CONTEXT_PRIORITY_REALTIME_NV } else { egl::CONTEXT_PRIORITY_HIGH_IMG } }, }; attrs.push(egl::CONTEXT_PRIORITY_LEVEL_IMG as EGLint); attrs.push(priority as EGLint); } attrs.push(egl::NONE as EGLint); let shared_context = if let Some(shared_context) = context_attributes.shared_context.as_ref() { match shared_context { RawContext::Egl(shared_context) => *shared_context, #[allow(unreachable_patterns)] _ => return Err(ErrorKind::NotSupported("passed incompatible raw context").into()), } } else { egl::NO_CONTEXT }; // Bind the api. unsafe { if self.inner.egl.BindAPI(api) == egl::FALSE { return Err(super::check_error().err().unwrap()); } let config = config.clone(); let context = self.inner.egl.CreateContext( *self.inner.raw, *config.inner.raw, shared_context, attrs.as_ptr(), ); if context == egl::NO_CONTEXT { return Err(super::check_error().err().unwrap()); } let inner = ContextInner { display: self.clone(), config, raw: EglContext(context), api }; Ok(NotCurrentContext::new(inner)) } } } /// A wrapper around `EGLContext` that is known to be not current. #[derive(Debug)] pub struct NotCurrentContext { inner: ContextInner, } impl NotCurrentContext { /// Make a [`Self::PossiblyCurrentContext`] indicating that the context /// could be current on the thread. pub fn make_current_surfaceless(self) -> Result { self.inner.make_current_surfaceless()?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn new(inner: ContextInner) -> Self { Self { inner } } } impl NotCurrentGlContext for NotCurrentContext { type PossiblyCurrentContext = PossiblyCurrentContext; type Surface = Surface; fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext { PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } } fn make_current( self, surface: &Surface, ) -> Result { self.inner.make_current_draw_read(surface, surface)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn make_current_draw_read( self, surface_draw: &Surface, surface_read: &Surface, ) -> Result { self.inner.make_current_draw_read(surface_draw, surface_read)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } } impl GlContext for NotCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { self.inner.priority() } } impl GetGlConfig for NotCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for NotCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for NotCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Egl(*self.inner.raw) } } impl Sealed for NotCurrentContext {} /// A wrapper around `EGLContext` that could be current for the current thread. #[derive(Debug)] pub struct PossiblyCurrentContext { pub(crate) inner: ContextInner, _nosendsync: PhantomData, } impl PossiblyCurrentContext { /// Make this context current on the calling thread. pub fn make_current_surfaceless(&self) -> Result<()> { self.inner.make_current_surfaceless() } } impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; type Surface = Surface; fn make_not_current(self) -> Result { self.make_not_current_in_place()?; Ok(NotCurrentContext::new(self.inner)) } fn make_not_current_in_place(&self) -> Result<()> { self.inner.make_not_current() } fn is_current(&self) -> bool { unsafe { self.inner.bind_api(); self.inner.display.inner.egl.GetCurrentContext() == *self.inner.raw } } fn make_current(&self, surface: &Self::Surface) -> Result<()> { self.inner.make_current_draw_read(surface, surface) } fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()> { self.inner.make_current_draw_read(surface_draw, surface_read) } } impl GlContext for PossiblyCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { self.inner.priority() } } impl GetGlConfig for PossiblyCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for PossiblyCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for PossiblyCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Egl(*self.inner.raw) } } impl Sealed for PossiblyCurrentContext {} pub(crate) struct ContextInner { display: Display, config: Config, raw: EglContext, api: egl::types::EGLenum, } impl ContextInner { fn make_current_surfaceless(&self) -> Result<()> { unsafe { if self.display.inner.egl.MakeCurrent( *self.display.inner.raw, egl::NO_SURFACE, egl::NO_SURFACE, *self.raw, ) == egl::FALSE { super::check_error() } else { Ok(()) } } } fn make_current_draw_read( &self, surface_draw: &Surface, surface_read: &Surface, ) -> Result<()> { unsafe { let draw = surface_draw.raw; let read = surface_read.raw; if self.display.inner.egl.MakeCurrent(*self.display.inner.raw, draw, read, *self.raw) == egl::FALSE { super::check_error() } else { Ok(()) } } } fn make_not_current(&self) -> Result<()> { unsafe { self.bind_api(); if self.display.inner.egl.MakeCurrent( *self.display.inner.raw, egl::NO_SURFACE, egl::NO_SURFACE, egl::NO_CONTEXT, ) == egl::FALSE { super::check_error() } else { Ok(()) } } } fn context_api(&self) -> ContextApi { match self.query_attribute(egl::CONTEXT_CLIENT_TYPE as EGLint).map(|a| a as EGLenum) { Some(egl::OPENGL_API) => ContextApi::OpenGl(None), // Map the rest to the GLES. _ => ContextApi::Gles(None), } } fn priority(&self) -> Priority { match self.query_attribute(egl::CONTEXT_PRIORITY_LEVEL_IMG as EGLint).map(|a| a as EGLenum) { Some(egl::CONTEXT_PRIORITY_LOW_IMG) => Priority::Low, Some(egl::CONTEXT_PRIORITY_HIGH_IMG) => Priority::High, Some(egl::CONTEXT_PRIORITY_REALTIME_NV) => Priority::Realtime, _ => Priority::Medium, } } /// Query the context attribute. fn query_attribute(&self, attribute: EGLint) -> Option { unsafe { let mut attribute_value = 0; if self.display.inner.egl.QueryContext( self.display.inner.raw.cast(), self.raw.cast(), attribute, &mut attribute_value, ) == egl::FALSE { None } else { Some(attribute_value) } } } /// This function could panic, but it does that for sanity reasons. /// /// When we create context we bind api and then store it and rebind /// on functions requiring it, so if it fails it means that it worked /// before, but for some reason stopped working, which should not /// happen according to the specification. pub(crate) fn bind_api(&self) { unsafe { if self.display.inner.egl.QueryAPI() == self.api { return; } if self.display.inner.egl.BindAPI(self.api) == egl::FALSE { panic!("EGL Api couldn't be bound anymore."); } } } } impl Drop for ContextInner { fn drop(&mut self) { unsafe { self.display.inner.egl.DestroyContext(*self.display.inner.raw, *self.raw); } } } impl fmt::Debug for ContextInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") .field("display", &self.display.inner.raw) .field("config", &self.config.inner.raw) .field("raw", &self.raw) .finish() } } #[derive(Debug)] struct EglContext(EGLContext); // Impl only `Send` for EglContext. unsafe impl Send for EglContext {} impl Deref for EglContext { type Target = EGLContext; fn deref(&self) -> &Self::Target { &self.0 } } glutin-0.32.2/src/api/egl/device.rs000064400000000000000000000202641046102023000151320ustar 00000000000000//! Everything related to `EGLDevice`. use std::collections::HashSet; use std::ffi::CStr; use std::path::Path; use std::ptr; use glutin_egl_sys::egl; use glutin_egl_sys::egl::types::EGLDeviceEXT; use crate::error::{ErrorKind, Result}; use super::display::{extensions_from_ptr, get_extensions, CLIENT_EXTENSIONS}; use super::{Egl, EGL}; /// Wrapper for `EGLDevice`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Device { inner: EGLDeviceEXT, extensions: HashSet<&'static str>, name: Option<&'static str>, vendor: Option<&'static str>, } // SAFETY: An EGLDevice is immutable and valid for the lifetime of the EGL // library. unsafe impl Send for Device {} unsafe impl Sync for Device {} impl Device { /// Query the available devices. /// /// This function returns [`Err`] if the `EGL_EXT_device_query` and /// `EGL_EXT_device_enumeration` or `EGL_EXT_device_base` extensions are /// not available. pub fn query_devices() -> Result> { let egl = match EGL.as_ref() { Some(egl) => egl, None => return Err(ErrorKind::NotFound.into()), }; let client_extensions = CLIENT_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); // Querying devices requires EGL_EXT_device_enumeration or EGL_EXT_device_base. if !client_extensions.contains("EGL_EXT_device_base") { if !client_extensions.contains("EGL_EXT_device_enumeration") { return Err(ErrorKind::NotSupported( "Enumerating devices is not supported by the EGL instance", ) .into()); } // EGL_EXT_device_enumeration depends on EGL_EXT_device_query, // so also check that just in case. if !client_extensions.contains("EGL_EXT_device_query") { return Err(ErrorKind::NotSupported( "EGL_EXT_device_enumeration without EGL_EXT_device_query, buggy driver?", ) .into()); } } let mut device_count = 0; if unsafe { // The specification states: // > An EGL_BAD_PARAMETER error is generated if is // > less than or equal to zero unless is NULL, or if // > is NULL. // // The error will never be generated since num_devices is a pointer // to the count being queried. Therefore there is no need to check // the error. egl.QueryDevicesEXT(0, ptr::null_mut(), &mut device_count) == egl::FALSE } { super::check_error()?; // On failure, EGL_FALSE is returned. return Err(ErrorKind::NotSupported("Querying device count failed").into()); } let mut devices = Vec::with_capacity(device_count as usize); unsafe { let mut count = device_count; if egl.QueryDevicesEXT(device_count, devices.as_mut_ptr(), &mut count) == egl::FALSE { super::check_error()?; // On failure, EGL_FALSE is returned. return Err(ErrorKind::NotSupported("Querying devices failed").into()); } // SAFETY: EGL has initialized the vector for the number of devices. devices.set_len(device_count as usize); } Ok(devices.into_iter().flat_map(|ptr| Device::from_ptr(egl, ptr))) } /// Get the device extensions supported by this device. /// /// These extensions are distinct from the display extensions and should not /// be used interchangeably. pub fn extensions(&self) -> &HashSet<&'static str> { &self.extensions } /// Get the name of the device. /// /// This function will return [`None`] if the `EGL_EXT_device_query_name` /// device extension is not available. pub fn name(&self) -> Option<&'static str> { self.name } /// Get the vendor of the device. /// /// This function will return [`None`] if the `EGL_EXT_device_query_name` /// device extension is not available. pub fn vendor(&self) -> Option<&'static str> { self.vendor } /// Get a raw handle to the `EGLDevice`. pub fn raw_device(&self) -> EGLDeviceEXT { self.inner } /// Get the DRM primary or render device node path for this /// [`EGLDeviceEXT`]. /// /// Requires the [`EGL_EXT_device_drm`] extension. /// /// If the [`EGL_EXT_device_drm_render_node`] extension is supported, this /// is guaranteed to return the **primary** device node path, or [`None`]. /// Consult [`Self::drm_render_device_node_path()`] to retrieve the /// **render** device node path. /// /// [`EGL_EXT_device_drm`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm.txt /// [`EGL_EXT_device_drm_render_node`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm_render_node.txt pub fn drm_device_node_path(&self) -> Option<&'static Path> { if !self.extensions.contains("EGL_EXT_device_drm") { return None; } // SAFETY: We pass a valid EGLDevice pointer, and validated that the enum name // is valid because the extension is present. unsafe { Self::query_string(self.raw_device(), egl::DRM_DEVICE_FILE_EXT) }.map(Path::new) } /// Get the DRM render device node path for this [`EGLDeviceEXT`]. /// /// Requires the [`EGL_EXT_device_drm_render_node`] extension. /// /// If the [`EGL_EXT_device_drm`] extension is supported in addition to /// [`EGL_EXT_device_drm_render_node`], /// consult [`Self::drm_device_node_path()`] to retrieve the **primary** /// device node path. /// /// [`EGL_EXT_device_drm`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm.txt /// [`EGL_EXT_device_drm_render_node`]: https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_device_drm_render_node.txt pub fn drm_render_device_node_path(&self) -> Option<&'static Path> { if !self.extensions.contains("EGL_EXT_device_drm_render_node") { return None; } const EGL_DRM_RENDER_NODE_PATH_EXT: egl::types::EGLenum = 0x3377; // SAFETY: We pass a valid EGLDevice pointer, and validated that the enum name // is valid because the extension is present. unsafe { Self::query_string(self.raw_device(), EGL_DRM_RENDER_NODE_PATH_EXT) } .map(Path::new) } /// # Safety /// The caller must pass a valid `egl_device` pointer and must ensure that /// `name` is valid for this device, i.e. by guaranteeing that the /// extension that introduces it is present. /// /// The returned string is `'static` for the lifetime of the globally loaded /// EGL library in [`EGL`]. unsafe fn query_string( egl_device: EGLDeviceEXT, name: egl::types::EGLenum, ) -> Option<&'static str> { let egl = super::EGL.as_ref().unwrap(); // SAFETY: The caller has ensured the name is valid. let ptr = unsafe { egl.QueryDeviceStringEXT(egl_device, name as _) }; if ptr.is_null() { return None; } unsafe { CStr::from_ptr(ptr) }.to_str().ok() } pub(crate) fn from_ptr(egl: &Egl, ptr: EGLDeviceEXT) -> Result { // SAFETY: The EGL specification guarantees the returned string is // static and null terminated: // // > eglQueryDeviceStringEXT returns a pointer to a static, // > zero-terminated string describing some aspect of the specified // > EGLDeviceEXT. must be EGL_EXTENSIONS. let extensions = unsafe { extensions_from_ptr(egl.QueryDeviceStringEXT(ptr, egl::EXTENSIONS as _)) }; let (name, vendor) = if extensions.contains("EGL_EXT_device_query_name") { // SAFETY: RENDERER_EXT and VENDOR are valid strings for device string queries // if EGL_EXT_device_query_name. unsafe { (Self::query_string(ptr, egl::RENDERER_EXT), Self::query_string(ptr, egl::VENDOR)) } } else { (None, None) }; Ok(Self { inner: ptr, extensions, name, vendor }) } } glutin-0.32.2/src/api/egl/display.rs000064400000000000000000000744741046102023000153540ustar 00000000000000//! Everything related to `EGLDisplay`. use std::collections::HashSet; use std::ffi::{self, CStr}; use std::fmt; use std::mem::MaybeUninit; use std::ops::Deref; use std::os::raw::c_char; use std::sync::Arc; use glutin_egl_sys::egl; use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; use once_cell::sync::OnceCell; use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle}; use crate::config::ConfigTemplate; use crate::context::Version; use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; use super::config::Config; use super::context::NotCurrentContext; use super::device::Device; use super::surface::Surface; use super::{Egl, EGL}; /// Extensions that don't require any display. pub(crate) static CLIENT_EXTENSIONS: OnceCell> = OnceCell::new(); /// A wrapper for the `EGLDisplay` and its supported extensions. #[derive(Debug, Clone)] pub struct Display { // Inner display to simplify passing it around. pub(crate) inner: Arc, } impl Display { /// Create EGL display with the native display. /// /// # Safety /// /// `raw_display` must point to a valid system display. Using zero or /// [`std::ptr::null()`] for the display will result in using /// `EGL_DEFAULT_DISPLAY`, which is not recommended or will /// work on a platform with a concept of native display, like Wayland. pub unsafe fn new(raw_display: RawDisplayHandle) -> Result { let egl = match EGL.as_ref() { Some(egl) => egl, None => return Err(ErrorKind::NotFound.into()), }; CLIENT_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); // Create a EGL display by chaining all display creation functions aborting on // `EGL_BAD_ATTRIBUTE`. let display = Self::get_platform_display(egl, raw_display) .or_else(|err| { if err.error_kind() == ErrorKind::BadAttribute { Err(err) } else { Self::get_platform_display_ext(egl, raw_display) } }) .or_else(|err| { if err.error_kind() == ErrorKind::BadAttribute { Err(err) } else { Self::get_display(egl, raw_display) } })?; Self::initialize_display(egl, display, Some(raw_display)) } /// Create an EGL display using the specified device. /// /// In most cases, prefer [`Display::new()`] unless you need to render /// off screen or use other extensions like EGLStreams. /// /// This function may take an optional [`RawDisplayHandle`] argument. At the /// moment the `raw_display` argument is ignored and this function will /// return [`Err`]. This may change in the future. /// /// # Safety /// /// If `raw_display` is [`Some`], `raw_display` must point to a valid /// [`RawDisplayHandle::Drm`]. The provided /// [`raw_display_handle::DrmDisplayHandle.fd`] may be closed after calling /// this function. pub unsafe fn with_device( device: &Device, raw_display: Option, ) -> Result { let egl = match EGL.as_ref() { Some(egl) => egl, None => return Err(ErrorKind::NotFound.into()), }; if !egl.GetPlatformDisplayEXT.is_loaded() { return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into()); } // Okay to unwrap here because the client extensions must have been enumerated // while querying the available devices or the device was gotten from an // existing display. let extensions = CLIENT_EXTENSIONS.get().unwrap(); if !extensions.contains("EGL_EXT_platform_base") && !extensions.contains("EGL_EXT_platform_device") { return Err(ErrorKind::NotSupported( "Creating a display from a device is not supported", ) .into()); } let mut attrs = Vec::::with_capacity(3); match raw_display { Some(RawDisplayHandle::Drm(handle)) if device.extensions().contains("EGL_EXT_device_drm") => { attrs.push(egl::DRM_MASTER_FD_EXT as EGLint); attrs.push(handle.fd as EGLint); }, Some(_) => { return Err(ErrorKind::NotSupported( "`egl::display::Display::with_device()` does not support \ non-`DrmDisplayHandle` `RawDisplayHandle`s", ) .into()) }, None => {}, }; // Push at the end so we can pop it on failure let mut has_display_reference = extensions.contains("EGL_KHR_display_reference"); if has_display_reference { attrs.push(egl::TRACK_REFERENCES_KHR as _); attrs.push(egl::TRUE as _); } // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLint); // NOTE: This fallback is needed because libglvnd advertises client extensions // if at least one vendor library supports them. This leads to creation // failures for the vendor libraries not supporting // EGL_KHR_display_reference. Also according to the spec creation is allowed // to fail with EGL_KHR_display_reference set to EGL_TRUE even if // EGL_KHR_display_reference is advertised in the client extension // string, so just always try creation without EGL_KHR_display_reference // if it failed using it. let platform_display = loop { match Self::check_display_error(unsafe { egl.GetPlatformDisplayEXT( egl::PLATFORM_DEVICE_EXT, device.raw_device() as *mut _, attrs.as_ptr(), ) }) { Err(_) if has_display_reference => { attrs.pop(); attrs.pop(); attrs.pop(); attrs.push(egl::NONE as EGLint); has_display_reference = false; }, platform_display => break platform_display, } } .map(EglDisplay::Ext)?; Self::initialize_display(egl, platform_display, None) } /// Get the [`Device`] the display is using. /// /// This function returns [`Err`] if the `EGL_EXT_device_query` or /// `EGL_EXT_device_base` extensions are not available. pub fn device(&self) -> Result { let no_display_extensions = CLIENT_EXTENSIONS.get().unwrap(); // Querying the device of a display only requires EGL_EXT_device_query, but we // also check if EGL_EXT_device_base is available since // EGL_EXT_device_base also provides EGL_EXT_device_query. if !no_display_extensions.contains("EGL_EXT_device_query") || !no_display_extensions.contains("EGL_EXT_device_base") { return Err(ErrorKind::NotSupported( "Querying the device from a display is not supported", ) .into()); } let mut device = MaybeUninit::uninit(); if unsafe { self.inner.egl.QueryDisplayAttribEXT( *self.inner.raw, egl::DEVICE_EXT as EGLint, device.as_mut_ptr(), ) } == egl::FALSE { // Check for EGL_NOT_INITIALIZED in case the display was externally terminated. // // EGL_BAD_ATTRIBUTE shouldn't be returned since EGL_DEVICE_EXT should be a // valid display attribute. return Err(super::check_error().err().unwrap_or_else(|| { ErrorKind::NotSupported("failed to query device from display").into() })); } let device = unsafe { device.assume_init() } as egl::types::EGLDeviceEXT; debug_assert_ne!( device, egl::NO_DEVICE_EXT, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) should never return EGL_NO_DEVICE_EXT on \ success" ); Device::from_ptr(self.inner.egl, device) } /// Get a reference to the initialized EGL API. pub fn egl(&self) -> &'static Egl { self.inner.egl } /// Terminate the EGL display. /// /// When the display is managed by glutin with the /// `EGL_KHR_display_reference` this function does nothing and /// `eglTerminate` will be automatically invoked during display destruction. /// /// # Safety /// /// This function will destroy the global EGL state, even the one created /// and managed by other libraries. Use this function only when you're /// bringing everything down. pub unsafe fn terminate(self) { if !self.inner.uses_display_reference() { unsafe { self.inner.egl.Terminate(*self.inner.raw); } } } fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result { if !egl.GetPlatformDisplay.is_loaded() { return Err(ErrorKind::NotSupported("eglGetPlatformDisplay is not supported").into()); } let extensions = CLIENT_EXTENSIONS.get().unwrap(); let mut attrs = Vec::::with_capacity(5); let (platform, display) = match display { RawDisplayHandle::Wayland(handle) if extensions.contains("EGL_KHR_platform_wayland") => { (egl::PLATFORM_WAYLAND_KHR, handle.display.as_ptr()) }, RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => { attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib); attrs.push(handle.screen as EGLAttrib); ( egl::PLATFORM_X11_KHR, handle.display.map_or(egl::DEFAULT_DISPLAY as *mut _, |d| d.as_ptr()), ) }, RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => { (egl::PLATFORM_GBM_KHR, handle.gbm_device.as_ptr()) }, RawDisplayHandle::Drm(_) => { return Err(ErrorKind::NotSupported( "`DrmDisplayHandle` must be used with `egl::display::Display::with_device()`", ) .into()) }, RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => { (egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _) }, _ => { return Err( ErrorKind::NotSupported("provided display handle is not supported").into() ) }, }; // Push at the end so we can pop it on failure let mut has_display_reference = extensions.contains("EGL_KHR_display_reference"); if has_display_reference { attrs.push(egl::TRACK_REFERENCES_KHR as _); attrs.push(egl::TRUE as _); } // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLAttrib); // NOTE: This fallback is needed because libglvnd advertises client extensions // if at least one vendor library supports them. This leads to creation // failures for the vendor libraries not supporting // EGL_KHR_display_reference. Also according to the spec creation is allowed // to fail with EGL_KHR_display_reference set to EGL_TRUE even if // EGL_KHR_display_reference is advertised in the client extension // string, so just always try creation without EGL_KHR_display_reference // if it failed using it. let platform_display = loop { match Self::check_display_error(unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) }) { Err(_) if has_display_reference => { attrs.pop(); attrs.pop(); attrs.pop(); attrs.push(egl::NONE as EGLAttrib); has_display_reference = false; }, platform_display => break platform_display, } }; platform_display.map(EglDisplay::Khr) } fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result { if !egl.GetPlatformDisplayEXT.is_loaded() { return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into()); } let extensions = CLIENT_EXTENSIONS.get().unwrap(); let mut attrs = Vec::::with_capacity(5); let mut legacy = false; let (platform, display) = match display { RawDisplayHandle::Wayland(handle) if extensions.contains("EGL_EXT_platform_wayland") => { (egl::PLATFORM_WAYLAND_EXT, handle.display.as_ptr()) }, RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11") => { attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint); attrs.push(handle.screen as EGLint); ( egl::PLATFORM_X11_EXT, handle.display.map_or(egl::DEFAULT_DISPLAY as *mut _, |d| d.as_ptr()), ) }, RawDisplayHandle::Xcb(handle) if extensions.contains("EGL_MESA_platform_xcb") || extensions.contains("EGL_EXT_platform_xcb") => { attrs.push(egl::PLATFORM_XCB_SCREEN_EXT as EGLint); attrs.push(handle.screen as EGLint); ( egl::PLATFORM_XCB_EXT, handle.connection.map_or(egl::DEFAULT_DISPLAY as *mut _, |c| c.as_ptr()), ) }, RawDisplayHandle::Gbm(handle) // NOTE: Some drivers report that they support the KHR GBM extension without EGL // 1.5 client, so work around that here by checking the KHR GBM extension as well. // The MESA and KHR extensions have the same constant values, thus it'll work // regardless. // // See https://github.com/rust-windowing/glutin/issues/1708. if extensions.contains("EGL_MESA_platform_gbm") || extensions.contains("EGL_KHR_platform_gbm") => { (egl::PLATFORM_GBM_MESA, handle.gbm_device.as_ptr()) }, RawDisplayHandle::Drm(_) => { return Err(ErrorKind::NotSupported( "`DrmDisplayHandle` must be used with `egl::display::Display::with_device()`", ) .into()) }, RawDisplayHandle::Windows(..) if extensions.contains("EGL_ANGLE_platform_angle") => { // Only CreateWindowSurface appears to work with Angle. legacy = true; (egl::PLATFORM_ANGLE_ANGLE, egl::DEFAULT_DISPLAY as *mut _) }, _ => { return Err( ErrorKind::NotSupported("provided display handle is not supported").into() ) }, }; // Push at the end so we can pop it on failure let mut has_display_reference = extensions.contains("EGL_KHR_display_reference"); if has_display_reference { attrs.push(egl::TRACK_REFERENCES_KHR as _); attrs.push(egl::TRUE as _); } // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLint); // NOTE: This fallback is needed because libglvnd advertises client extensions // if at least one vendor library supports them. This leads to creation // failures for the vendor libraries not supporting // EGL_KHR_display_reference. Also according to the spec creation is allowed // to fail with EGL_KHR_display_reference set to EGL_TRUE even if // EGL_KHR_display_reference is advertised in the client extension // string, so just always try creation without EGL_KHR_display_reference // if it failed using it. let platform_display = loop { match Self::check_display_error(unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) }) { Err(_) if has_display_reference => { attrs.pop(); attrs.pop(); attrs.pop(); attrs.push(egl::NONE as EGLint); has_display_reference = false; }, platform_display => break platform_display, } }; platform_display.map(|display| { if legacy { // NOTE: For angle we use the Legacy code path, as that uses CreateWindowSurface // instead of CreatePlatformWindowSurface*. The latter somehow // doesn't work, only the former does. But Angle's own example also use the // former: https://github.com/google/angle/blob/main/util/EGLWindow.cpp#L424 EglDisplay::Legacy(display) } else { EglDisplay::Ext(display) } }) } fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result { let display = match display { RawDisplayHandle::Gbm(handle) => handle.gbm_device.as_ptr(), RawDisplayHandle::Drm(_) => { return Err(ErrorKind::NotSupported( "`DrmDisplayHandle` must be used with `egl::display::Display::with_device()`", ) .into()) }, RawDisplayHandle::Xlib(XlibDisplayHandle { display, .. }) => { display.map_or(egl::DEFAULT_DISPLAY as *mut _, |d| d.as_ptr()) }, RawDisplayHandle::Android(_) | RawDisplayHandle::Ohos(_) => { egl::DEFAULT_DISPLAY as *mut _ }, _ => { return Err( ErrorKind::NotSupported("provided display handle is not supported").into() ) }, }; let display = unsafe { egl.GetDisplay(display) }; Self::check_display_error(display).map(EglDisplay::Legacy) } fn extract_display_features( extensions: &HashSet<&'static str>, version: Version, ) -> DisplayFeatures { // Extract features. let mut supported_features = DisplayFeatures::CREATE_ES_CONTEXT | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS | DisplayFeatures::SWAP_CONTROL; supported_features.set( DisplayFeatures::FLOAT_PIXEL_FORMAT, extensions.contains("EGL_EXT_pixel_format_float"), ); supported_features .set(DisplayFeatures::SRGB_FRAMEBUFFERS, extensions.contains("EGL_KHR_gl_colorspace")); supported_features.set( DisplayFeatures::CONTEXT_ROBUSTNESS, version > Version::new(1, 5) || extensions.contains("EGL_EXT_create_context_robustness"), ); supported_features.set( DisplayFeatures::CONTEXT_NO_ERROR, extensions.contains("EGL_KHR_create_context_no_error"), ); supported_features } fn check_display_error(display: EGLDisplay) -> Result { if display == egl::NO_DISPLAY { // XXX the specification is a bit vague here, so fallback instead of hard // assert. Err(super::check_error().err().unwrap_or_else(|| { ErrorKind::NotSupported("failed to create EGLDisplay without a reason").into() })) } else { Ok(display) } } fn initialize_display( egl: &'static Egl, display: EglDisplay, raw_display_handle: Option, ) -> Result { let version = unsafe { let (mut major, mut minor) = (0, 0); if egl.Initialize(*display, &mut major, &mut minor) == egl::FALSE { return Err(super::check_error().expect_err("eglInit failed without a reason")); } Version::new(major as u8, minor as u8) }; let display = match display { // `eglGetPlatformDisplay` and `GetPlatformDisplayEXT` aren't really differentiated, // we must check if the version of the initialized display is not sensible for the // EglDisplay type and downgrade it if so. EglDisplay::Khr(display) if version <= Version { major: 1, minor: 4 } => { let client_extensions = CLIENT_EXTENSIONS.get().unwrap(); if client_extensions.contains("EGL_EXT_platform_base") && (version == Version { major: 1, minor: 4 }) { // `EGL_EXT_platform_base` requires EGL 1.4 per specification; we cannot safely // presume that an `Ext` display would be valid for older versions. EglDisplay::Ext(display) } else { EglDisplay::Legacy(display) } }, // We do not do anything otherwise. display => display, }; // Load extensions. let display_extensions = get_extensions(egl, *display); let features = Self::extract_display_features(&display_extensions, version); let inner = Arc::new(DisplayInner { egl, raw: display, _native_display: raw_display_handle.map(NativeDisplay), version, display_extensions, features, }); Ok(Self { inner }) } } impl GlDisplay for Display { type Config = Config; type NotCurrentContext = NotCurrentContext; type PbufferSurface = Surface; type PixmapSurface = Surface; type WindowSurface = Surface; unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( &self, config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { unsafe { self.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ } } fn version_string(&self) -> String { format!("EGL {}.{}", self.inner.version.major, self.inner.version.minor) } fn supported_features(&self) -> DisplayFeatures { self.inner.features } } impl GetDisplayExtensions for Display { fn extensions(&self) -> &HashSet<&'static str> { &self.inner.display_extensions } } impl AsRawDisplay for Display { fn raw_display(&self) -> RawDisplay { RawDisplay::Egl(*self.inner.raw) } } impl Sealed for Display {} pub(crate) struct DisplayInner { /// Pointer to the EGL handler to simplify API calls. pub(crate) egl: &'static Egl, /// Pointer to the egl display. pub(crate) raw: EglDisplay, /// The version of the egl library. pub(crate) version: Version, /// Display EGL extensions. pub(crate) display_extensions: HashSet<&'static str>, /// The features supported by the display. pub(crate) features: DisplayFeatures, /// The raw display used to create EGL display. pub(crate) _native_display: Option, } impl DisplayInner { fn uses_display_reference(&self) -> bool { if !CLIENT_EXTENSIONS.get().unwrap().contains("EGL_KHR_display_reference") { return false; } // If the EGL_TRACK_REFERENCES_KHR attribute is true, then EGL will internally // reference count the display. If that is the case, glutin can // terminate the display without worry for the instance being // reused elsewhere. let mut track_references = MaybeUninit::::uninit(); (match self.raw { EglDisplay::Khr(khr) => unsafe { self.egl.QueryDisplayAttribKHR( khr, egl::TRACK_REFERENCES_KHR as _, track_references.as_mut_ptr(), ) }, EglDisplay::Ext(ext) => unsafe { self.egl.QueryDisplayAttribEXT( ext, egl::TRACK_REFERENCES_KHR as _, track_references.as_mut_ptr(), ) }, EglDisplay::Legacy(_) => egl::FALSE, } == egl::TRUE) } } impl fmt::Debug for DisplayInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Display") .field("raw", &self.raw) .field("version", &self.version) .field("features", &self.features) .field("extensions", &self.display_extensions) .finish() } } impl Drop for DisplayInner { fn drop(&mut self) { if self.uses_display_reference() { unsafe { self.egl.Terminate(*self.raw); } } // We cannot call safely call `eglTerminate`. // // This may sound confusing, but this is a result of how EGL works: // // From the documentation of `eglGetDisplay`: // > Multiple calls made to eglGetDisplay with the same display_id will // > return the same EGLDisplay handle. // // And from the documentation of `eglGetPlatformDisplay`: // > Multiple calls made to eglGetPlatformDisplay with the same // > parameters will return the same // > EGLDisplay handle. // // Furthermore the following is done when a display is initialized: // > Initializing an already initialized EGL display connection has no // > effect besides returning the // > version numbers. // // Terminating a display connection and then creating a new display // connection will reference the same display. This effectively // makes an EGLDisplay a singleton for the specified display_id or // platform and native display. // // Because EGLDisplay is a singleton, this makes the following sequence // problematic: // // 1. A display is created for a platform // 2. A second display is created for the same platform // 3. The first display is dropped, resulting in eglTerminate being // called. // 4. A context created from the second display is made // current, but it has been terminated and returns an EGL_BAD_DISPLAY // error. // // But wait? This causes a memory leak! // // Yes it does indeed result in a memory leak since we do not terminate // displays on drop. For most applications there is only ever a // single EGLDisplay for the lifetime of the application. The cost // of not dropping the display is negligible because the display will // probably be destroyed on app termination and we can let the // operating system deal with tearing down EGL instead. } } #[derive(Debug, Clone, Copy)] pub(crate) struct NativeDisplay(RawDisplayHandle); unsafe impl Send for NativeDisplay {} unsafe impl Sync for NativeDisplay {} impl Deref for NativeDisplay { type Target = RawDisplayHandle; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(Debug, Clone)] pub(crate) enum EglDisplay { /// The display was created with the KHR extension. Khr(EGLDisplay), /// The display was created with the EXT extension. Ext(EGLDisplay), /// The display in use is a legacy variant. Legacy(EGLDisplay), } // The EGL display could be shared between threads. unsafe impl Send for EglDisplay {} unsafe impl Sync for EglDisplay {} impl Deref for EglDisplay { type Target = EGLDisplay; fn deref(&self) -> &Self::Target { match self { EglDisplay::Khr(display) => display, EglDisplay::Ext(display) => display, EglDisplay::Legacy(display) => display, } } } /// Collect EGL extensions for the given `display`. pub(crate) fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { unsafe { let extensions = egl.QueryString(display, egl::EXTENSIONS as i32); // SAFETY: The EGL specification guarantees the returned string is // static and null terminated: // // > eglQueryString returns a pointer to a static, zero-terminated // > string describing properties of the EGL client or of an EGL // > display connection. extensions_from_ptr(extensions) } } /// # Safety /// /// - The `extensions` pointer must be NULL (representing no extensions) or it /// must be non-null and contain a static, null terminated C string. pub(crate) unsafe fn extensions_from_ptr(extensions: *const c_char) -> HashSet<&'static str> { if extensions.is_null() { return HashSet::new(); } // SAFETY: The caller has ensured the string pointer is null terminated. if let Ok(extensions) = unsafe { CStr::from_ptr(extensions) }.to_str() { extensions.split(' ').collect::>() } else { HashSet::new() } } glutin-0.32.2/src/api/egl/mod.rs000064400000000000000000000101611046102023000144450ustar 00000000000000//! EGL platform Api. //! //! This platform is typically available on Linux, Android and other Unix-like //! platforms. //! //! The EGL platform allows creating a [`Display`](self::display::Display) from //! a [`Device`](self::device::Device). use std::ffi::{self, CString}; use std::ops::{Deref, DerefMut}; use glutin_egl_sys::egl; use libloading::Library; use once_cell::sync::{Lazy, OnceCell}; #[cfg(unix)] use libloading::os::unix as libloading_os; #[cfg(windows)] use libloading::os::windows as libloading_os; use crate::error::{Error, ErrorKind, Result}; use crate::lib_loading::{SymLoading, SymWrapper}; pub mod config; pub mod context; pub mod device; pub mod display; pub mod surface; // WARNING: If this implementation is ever changed to unload or replace the // library, note that public API functions currently retirm `&'static str`ings // out of it, which would become invalid. pub(crate) static EGL: Lazy> = Lazy::new(|| { #[cfg(windows)] let paths = ["libEGL.dll", "atioglxx.dll"]; #[cfg(not(windows))] let paths = ["libEGL.so.1", "libEGL.so"]; unsafe { SymWrapper::new(&paths).map(Egl).ok() } }); type EglGetProcAddress = unsafe extern "system" fn(*const ffi::c_void) -> *const ffi::c_void; static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); /// EGL interface. #[allow(missing_debug_implementations)] pub struct Egl(SymWrapper); unsafe impl Sync for Egl {} unsafe impl Send for Egl {} impl SymLoading for egl::Egl { unsafe fn load_with(lib: &Library) -> Self { let loader = move |sym_name: &'static str| -> *const ffi::c_void { unsafe { let sym_name = CString::new(sym_name.as_bytes()).unwrap(); if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { return *sym; } let egl_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| { let sym: libloading::Symbol<'_, EglGetProcAddress> = lib.get(b"eglGetProcAddress\0").unwrap(); sym.into_raw() }); // The symbol was not available in the library, so ask eglGetProcAddress for it. // Note that eglGetProcAddress was only able to look up extension // functions prior to EGL 1.5, hence this two-part dance. (egl_proc_address)(sym_name.as_bytes_with_nul().as_ptr() as *const ffi::c_void) } }; egl::BindWaylandDisplayWL::load_with(loader); egl::UnbindWaylandDisplayWL::load_with(loader); egl::QueryWaylandBufferWL::load_with(loader); egl::CreateWaylandBufferFromImageWL::load_with(loader); Self::load_with(loader) } } impl Deref for Egl { type Target = egl::Egl; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Egl { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } /// Obtain the error from the EGL. fn check_error() -> Result<()> { let egl = EGL.as_ref().unwrap(); unsafe { let raw_code = egl.GetError() as egl::types::EGLenum; let kind = match raw_code { egl::SUCCESS => return Ok(()), egl::NOT_INITIALIZED => ErrorKind::InitializationFailed, egl::BAD_ACCESS => ErrorKind::BadAccess, egl::BAD_ALLOC => ErrorKind::OutOfMemory, egl::BAD_ATTRIBUTE => ErrorKind::BadAttribute, egl::BAD_CONTEXT => ErrorKind::BadContext, egl::BAD_CONFIG => ErrorKind::BadConfig, egl::BAD_CURRENT_SURFACE => ErrorKind::BadCurrentSurface, egl::BAD_DISPLAY => ErrorKind::BadDisplay, egl::BAD_SURFACE => ErrorKind::BadSurface, egl::BAD_MATCH => ErrorKind::BadMatch, egl::BAD_PARAMETER => ErrorKind::BadParameter, egl::BAD_NATIVE_PIXMAP => ErrorKind::BadNativePixmap, egl::BAD_NATIVE_WINDOW => ErrorKind::BadNativeWindow, egl::CONTEXT_LOST => ErrorKind::ContextLost, _ => ErrorKind::Misc, }; Err(Error::new(Some(raw_code as i64), None, kind)) } } glutin-0.32.2/src/api/egl/surface.rs000064400000000000000000000500461046102023000153240ustar 00000000000000//! Everything related to `EGLSurface`. use std::marker::PhantomData; use std::num::NonZeroU32; use std::{ffi, fmt}; use glutin_egl_sys::egl; use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface, EGLint}; use raw_window_handle::RawWindowHandle; #[cfg(wayland_platform)] use wayland_sys::{egl::*, ffi_dispatch}; use crate::api::egl::display::EglDisplay; use crate::config::GetGlConfig; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{ AsRawSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, Rect, SurfaceAttributes, SurfaceTypeTrait, SwapInterval, WindowSurface, }; use super::config::Config; use super::context::PossiblyCurrentContext; use super::display::Display; /// Hint for the attribute list size. const ATTR_SIZE_HINT: usize = 8; impl Display { pub(crate) unsafe fn create_pbuffer_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let width = surface_attributes.width.unwrap(); let height = surface_attributes.height.unwrap(); // XXX Window surface is using `EGLAttrib` and not `EGLint`. let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); // Add dimensions. attrs.push(egl::WIDTH as EGLint); attrs.push(width.get() as EGLint); attrs.push(egl::HEIGHT as EGLint); attrs.push(height.get() as EGLint); // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLint); let config = config.clone(); let surface = unsafe { Self::check_surface_error(self.inner.egl.CreatePbufferSurface( *self.inner.raw, *config.inner.raw, attrs.as_ptr(), ))? }; Ok(Surface { display: self.clone(), native_window: None, config, raw: surface, _ty: PhantomData, }) } pub(crate) unsafe fn create_pixmap_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); if surface_attributes.srgb.is_some() && config.srgb_capable() { attrs.push(egl::GL_COLORSPACE as EGLAttrib); let colorspace = match surface_attributes.srgb { Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, }; attrs.push(colorspace); } // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLAttrib); let config = config.clone(); let surface = match self.inner.raw { EglDisplay::Khr(display) => { let platform_pixmap = native_pixmap.as_platform_pixmap(); if platform_pixmap.is_null() { return Err(ErrorKind::BadNativePixmap.into()); } unsafe { self.inner.egl.CreatePlatformPixmapSurface( display, *config.inner.raw, platform_pixmap, attrs.as_ptr(), ) } }, EglDisplay::Ext(display) => { let platform_pixmap = native_pixmap.as_platform_pixmap(); if platform_pixmap.is_null() { return Err(ErrorKind::BadNativePixmap.into()); } unsafe { let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); self.inner.egl.CreatePlatformPixmapSurfaceEXT( display, *config.inner.raw, platform_pixmap, attrs.as_ptr(), ) } }, EglDisplay::Legacy(display) => { let native_pixmap = native_pixmap.as_native_pixmap(); #[cfg(not(windows))] if native_pixmap.is_null() { return Err(ErrorKind::BadNativePixmap.into()); } #[cfg(windows)] if native_pixmap == 0 { return Err(ErrorKind::BadNativePixmap.into()); } unsafe { // This call accepts raw value, instead of pointer. let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); self.inner.egl.CreatePixmapSurface( display, *config.inner.raw, native_pixmap, attrs.as_ptr(), ) } }, }; let surface = Self::check_surface_error(surface)?; Ok(Surface { display: self.clone(), config, native_window: None, raw: surface, _ty: PhantomData, }) } pub(crate) unsafe fn create_window_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { // Create native window. let native_window = NativeWindow::new( surface_attributes.width.unwrap(), surface_attributes.height.unwrap(), surface_attributes.raw_window_handle.as_ref().unwrap(), )?; // XXX Window surface is using `EGLAttrib` and not `EGLint`. let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); // Add information about render buffer. attrs.push(egl::RENDER_BUFFER as EGLAttrib); let buffer = if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER } as EGLAttrib; attrs.push(buffer); // // Add colorspace if the extension is present. if surface_attributes.srgb.is_some() && config.srgb_capable() { attrs.push(egl::GL_COLORSPACE as EGLAttrib); let colorspace = match surface_attributes.srgb { Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, }; attrs.push(colorspace); } // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLAttrib); let config = config.clone(); let surface = match self.inner.raw { EglDisplay::Khr(display) => unsafe { self.inner.egl.CreatePlatformWindowSurface( display, *config.inner.raw, native_window.as_platform_window(), attrs.as_ptr(), ) }, EglDisplay::Ext(display) => unsafe { let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); self.inner.egl.CreatePlatformWindowSurfaceEXT( display, *config.inner.raw, native_window.as_platform_window(), attrs.as_ptr(), ) }, EglDisplay::Legacy(display) => unsafe { let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); self.inner.egl.CreateWindowSurface( display, *config.inner.raw, native_window.as_native_window(), attrs.as_ptr(), ) }, }; let surface = Self::check_surface_error(surface)?; Ok(Surface { display: self.clone(), config, native_window: Some(native_window), raw: surface, _ty: PhantomData, }) } fn check_surface_error(surface: EGLSurface) -> Result { if surface == egl::NO_SURFACE { Err(super::check_error().err().unwrap()) } else { Ok(surface) } } } /// A wrapper around `EGLSurface`. pub struct Surface { display: Display, config: Config, pub(crate) raw: EGLSurface, native_window: Option, _ty: PhantomData, } // Impl only `Send` for Surface. unsafe impl Send for Surface {} impl Surface { /// Swaps the underlying back buffers when the surface is not single /// buffered and pass the [`Rect`] information to the system /// compositor. Providing empty slice will damage the entire surface. /// /// When the underlying extensions are not supported the function acts like /// [`Self::swap_buffers`]. /// /// This Api doesn't do any partial rendering, it just provides hints for /// the system compositor. pub fn swap_buffers_with_damage( &self, context: &PossiblyCurrentContext, rects: &[Rect], ) -> Result<()> { context.inner.bind_api(); let res = unsafe { if self.display.inner.display_extensions.contains("EGL_KHR_swap_buffers_with_damage") { self.display.inner.egl.SwapBuffersWithDamageKHR( *self.display.inner.raw, self.raw, rects.as_ptr() as *mut _, rects.len() as _, ) } else if self .display .inner .display_extensions .contains("EGL_EXT_swap_buffers_with_damage") { self.display.inner.egl.SwapBuffersWithDamageEXT( *self.display.inner.raw, self.raw, rects.as_ptr() as *mut _, rects.len() as _, ) } else { self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw) } }; if res == egl::FALSE { super::check_error() } else { Ok(()) } } /// # Safety /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint { unsafe { let mut value = 0; self.display.inner.egl.QuerySurface( *self.display.inner.raw, self.raw, attr, &mut value, ); value } } } impl Drop for Surface { fn drop(&mut self) { unsafe { self.display.inner.egl.DestroySurface(*self.display.inner.raw, self.raw); } } } impl GlSurface for Surface { type Context = PossiblyCurrentContext; type SurfaceType = T; fn buffer_age(&self) -> u32 { self.display .inner .display_extensions .contains("EGL_EXT_buffer_age") .then(|| unsafe { self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) }) .unwrap_or(0) as u32 } fn width(&self) -> Option { unsafe { Some(self.raw_attribute(egl::WIDTH as EGLint) as u32) } } fn height(&self) -> Option { unsafe { Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) } } fn is_single_buffered(&self) -> bool { unsafe { self.raw_attribute(egl::RENDER_BUFFER as EGLint) == egl::SINGLE_BUFFER as i32 } } fn swap_buffers(&self, context: &Self::Context) -> Result<()> { unsafe { context.inner.bind_api(); if self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw) == egl::FALSE { super::check_error() } else { Ok(()) } } } fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> { unsafe { context.inner.bind_api(); let interval = match interval { SwapInterval::DontWait => 0, SwapInterval::Wait(interval) => interval.get() as EGLint, }; if self.display.inner.egl.SwapInterval(*self.display.inner.raw, interval) == egl::FALSE { super::check_error() } else { Ok(()) } } } fn is_current(&self, context: &Self::Context) -> bool { self.is_current_draw(context) && self.is_current_read(context) } fn is_current_draw(&self, context: &Self::Context) -> bool { unsafe { context.inner.bind_api(); self.display.inner.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.raw } } fn is_current_read(&self, context: &Self::Context) -> bool { unsafe { context.inner.bind_api(); self.display.inner.egl.GetCurrentSurface(egl::READ as EGLint) == self.raw } } fn resize(&self, _context: &Self::Context, width: NonZeroU32, height: NonZeroU32) { self.native_window.as_ref().unwrap().resize(width, height) } } impl GetGlConfig for Surface { type Target = Config; fn config(&self) -> Self::Target { self.config.clone() } } impl GetGlDisplay for Surface { type Target = Display; fn display(&self) -> Self::Target { self.display.clone() } } impl AsRawSurface for Surface { fn raw_surface(&self) -> RawSurface { RawSurface::Egl(self.raw) } } impl fmt::Debug for Surface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Surface") .field("display", &self.display.inner.raw) .field("config", &self.config.inner.raw) .field("raw", &self.raw) .field("native_window", &self.native_window) .field("type", &T::surface_type()) .finish() } } impl Sealed for Surface {} #[derive(Debug)] enum NativeWindow { #[allow(dead_code)] Wayland(*mut ffi::c_void), Xlib(std::os::raw::c_ulong), Xcb(u32), Android(*mut ffi::c_void), Ohos(*mut ffi::c_void), Win32(isize), Gbm(*mut ffi::c_void), } impl NativeWindow { fn new( _width: NonZeroU32, _height: NonZeroU32, raw_window_handle: &RawWindowHandle, ) -> Result { let native_window = match raw_window_handle { #[cfg(wayland_platform)] RawWindowHandle::Wayland(window_handle) => unsafe { let ptr = ffi_dispatch!( wayland_egl_handle(), wl_egl_window_create, window_handle.surface.as_ptr().cast(), _width.get() as _, _height.get() as _ ); if ptr.is_null() { return Err(ErrorKind::OutOfMemory.into()); } Self::Wayland(ptr.cast()) }, RawWindowHandle::Xlib(window_handle) => { if window_handle.window == 0 { return Err(ErrorKind::BadNativeWindow.into()); } Self::Xlib(window_handle.window as _) }, RawWindowHandle::Xcb(window_handle) => Self::Xcb(window_handle.window.get() as _), RawWindowHandle::AndroidNdk(window_handle) => { Self::Android(window_handle.a_native_window.as_ptr()) }, RawWindowHandle::OhosNdk(window_handle) => { Self::Ohos(window_handle.native_window.as_ptr()) }, RawWindowHandle::Win32(window_handle) => Self::Win32(window_handle.hwnd.get() as _), RawWindowHandle::Gbm(window_handle) => Self::Gbm(window_handle.gbm_surface.as_ptr()), _ => { return Err( ErrorKind::NotSupported("provided native window is not supported").into() ) }, }; Ok(native_window) } fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) { #[cfg(wayland_platform)] if let Self::Wayland(wl_egl_surface) = self { unsafe { ffi_dispatch!( wayland_egl_handle(), wl_egl_window_resize, *wl_egl_surface as _, _width.get() as _, _height.get() as _, 0, 0 ) } } } /// Returns the underlying handle value. fn as_native_window(&self) -> egl::NativeWindowType { match *self { Self::Wayland(wl_egl_surface) => wl_egl_surface as egl::NativeWindowType, Self::Xlib(window_id) => window_id as egl::NativeWindowType, Self::Xcb(window_id) => window_id as egl::NativeWindowType, Self::Win32(hwnd) => hwnd as egl::NativeWindowType, Self::Android(a_native_window) => a_native_window as egl::NativeWindowType, Self::Ohos(native_window) => native_window as egl::NativeWindowType, Self::Gbm(gbm_surface) => gbm_surface as egl::NativeWindowType, } } /// Returns a pointer to the underlying handle value on X11, /// the raw underlying handle value on all other platforms. /// /// This exists because of a discrepancy in the new /// `eglCreatePlatformWindowSurface*` functions which take a pointer to the /// `window_id` on X11 and Xlib, in contrast to the legacy /// `eglCreateWindowSurface` which always takes the raw value. /// /// See also: /// /// /// # Safety /// /// On X11 the returned pointer is a cast of the `&self` borrow. fn as_platform_window(&self) -> *mut ffi::c_void { match self { Self::Wayland(wl_egl_surface) => *wl_egl_surface, Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void, Self::Xcb(window_id) => window_id as *const _ as *mut ffi::c_void, Self::Win32(hwnd) => *hwnd as *const ffi::c_void as *mut _, Self::Android(a_native_window) => *a_native_window, Self::Ohos(native_window) => *native_window, Self::Gbm(gbm_surface) => *gbm_surface, } } } #[cfg(wayland_platform)] impl Drop for NativeWindow { fn drop(&mut self) { unsafe { if let Self::Wayland(wl_egl_window) = self { ffi_dispatch!(wayland_egl_handle(), wl_egl_window_destroy, wl_egl_window.cast()); } } } } impl NativePixmap { /// Returns the underlying handle value. fn as_native_pixmap(&self) -> egl::NativePixmapType { match *self { Self::XlibPixmap(xid) => xid as egl::NativePixmapType, Self::XcbPixmap(xid) => xid as egl::NativePixmapType, Self::WindowsPixmap(hbitmap) => hbitmap as egl::NativePixmapType, } } /// Returns a pointer to the underlying handle value on X11, /// the raw underlying handle value on all other platforms. /// /// This exists because of a discrepancy in the new /// `eglCreatePlatformPixmapSurface*` functions which take a pointer to the /// `xid` on X11 and Xlib, in contrast to the legacy /// `eglCreatePixmapSurface` which always takes the raw value. /// /// See also: /// /// /// # Safety /// /// On X11 the returned pointer is a cast of the `&self` borrow. fn as_platform_pixmap(&self) -> *mut ffi::c_void { match self { Self::XlibPixmap(xid) => xid as *const _ as *mut _, Self::XcbPixmap(xid) => xid as *const _ as *mut _, Self::WindowsPixmap(hbitmap) => *hbitmap as *const ffi::c_void as *mut _, } } } glutin-0.32.2/src/api/glx/config.rs000064400000000000000000000277651046102023000152000ustar 00000000000000//! Everything related to finding and manipulating the `GLXFBConfig`. use std::ops::Deref; use std::os::raw::c_int; use std::sync::Arc; use std::{fmt, slice}; use glutin_glx_sys::glx::types::GLXFBConfig; use glutin_glx_sys::{glx, glx_extra}; use raw_window_handle::RawWindowHandle; use crate::config::{ Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB}; use crate::private::Sealed; use super::display::Display; impl Display { pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { let mut config_attributes = Vec::::new(); // Add color buffer type. match template.color_buffer_type { ColorBufferType::Rgb { r_size, g_size, b_size } => { // Type. config_attributes.push(glx::X_VISUAL_TYPE as c_int); config_attributes.push(glx::TRUE_COLOR as c_int); // R. config_attributes.push(glx::RED_SIZE as c_int); config_attributes.push(r_size as c_int); // G. config_attributes.push(glx::GREEN_SIZE as c_int); config_attributes.push(g_size as c_int); // B. config_attributes.push(glx::BLUE_SIZE as c_int); config_attributes.push(b_size as c_int); }, ColorBufferType::Luminance(luminance) => { // Type. config_attributes.push(glx::X_VISUAL_TYPE as c_int); config_attributes.push(glx::GRAY_SCALE as c_int); // L. config_attributes.push(glx::RED_SIZE as c_int); config_attributes.push(luminance as c_int); }, }; // Render type. config_attributes.push(glx::RENDER_TYPE as c_int); if template.float_pixels && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) { config_attributes.push(glx_extra::RGBA_FLOAT_BIT_ARB as c_int); } else if template.float_pixels { return Err(ErrorKind::NotSupported("float pixels are not supported").into()); } else { config_attributes.push(glx::RGBA_BIT as c_int); } // Add caveat. if let Some(hardware_accelerated) = template.hardware_accelerated { config_attributes.push(glx::CONFIG_CAVEAT as c_int); if hardware_accelerated { config_attributes.push(glx::NONE as c_int); } else { config_attributes.push(glx::SLOW_CONFIG as c_int); } } // Double buffer. config_attributes.push(glx::DOUBLEBUFFER as c_int); config_attributes.push(!template.single_buffering as c_int); // Add alpha. config_attributes.push(glx::ALPHA_SIZE as c_int); config_attributes.push(template.alpha_size as c_int); // Add depth. config_attributes.push(glx::DEPTH_SIZE as c_int); config_attributes.push(template.depth_size as c_int); // Add stencil. config_attributes.push(glx::STENCIL_SIZE as c_int); config_attributes.push(template.stencil_size as c_int); // Add visual if was provided. if let Some(RawWindowHandle::Xlib(window)) = template.native_window { if window.visual_id > 0 { config_attributes.push(glx::VISUAL_ID as c_int); config_attributes.push(window.visual_id as c_int); } } // Add surface type. config_attributes.push(glx::DRAWABLE_TYPE as c_int); let mut surface_type = 0; if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { surface_type |= glx::WINDOW_BIT; } if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { surface_type |= glx::PBUFFER_BIT; } if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { surface_type |= glx::PIXMAP_BIT; } config_attributes.push(surface_type as c_int); // Add maximum height of pbuffer. if let Some(pbuffer_width) = template.max_pbuffer_width { config_attributes.push(glx::MAX_PBUFFER_WIDTH as c_int); config_attributes.push(pbuffer_width as c_int); } // Add maximum width of pbuffer. if let Some(pbuffer_height) = template.max_pbuffer_height { config_attributes.push(glx::MAX_PBUFFER_HEIGHT as c_int); config_attributes.push(pbuffer_height as c_int); } // Add stereoscopy, if present. if let Some(stereoscopy) = template.stereoscopy { config_attributes.push(glx::STEREO as c_int); config_attributes.push(stereoscopy as c_int); } // Add multisampling. if let Some(num_samples) = template.num_samples { if self.inner.features.contains(DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS) { config_attributes.push(glx::SAMPLE_BUFFERS as c_int); config_attributes.push(1); config_attributes.push(glx::SAMPLES as c_int); config_attributes.push(num_samples as c_int); } } // Push X11 `None` to terminate the list. config_attributes.push(0); unsafe { let mut num_configs = 0; let raw_configs = self.inner.glx.ChooseFBConfig( self.inner.raw.cast(), self.inner.screen as _, config_attributes.as_ptr() as *const _, &mut num_configs, ); if raw_configs.is_null() { return Err(ErrorKind::BadConfig.into()); } let configs = slice::from_raw_parts_mut(raw_configs, num_configs as usize).to_vec(); // Free the memory from the Xlib, since we've just copied it. (XLIB.as_ref().unwrap().XFree)(raw_configs as *mut _); let iter = configs .into_iter() .map(move |raw| { let raw = GlxConfig(raw); let inner = Arc::new(ConfigInner { display: self.clone(), raw }); Config { inner } }) .filter(move |config| { !template.transparency || config.supports_transparency().unwrap_or(false) }); Ok(Box::new(iter)) } } } /// A wrapper around `GLXFBConfig`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { pub(crate) inner: Arc, } impl Config { /// # Safety /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: c_int) -> c_int { unsafe { let mut val = 0; self.inner.display.inner.glx.GetFBConfigAttrib( self.inner.display.inner.raw.cast(), *self.inner.raw, attr, &mut val, ); val as c_int } } pub(crate) fn is_single_buffered(&self) -> bool { unsafe { self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 } } } impl GlConfig for Config { fn color_buffer_type(&self) -> Option { unsafe { match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ { glx::TRUE_COLOR => { let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8; let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8; let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8; Some(ColorBufferType::Rgb { r_size, g_size, b_size }) }, glx::GRAY_SCALE => { let luma = self.raw_attribute(glx::RED_SIZE as c_int); Some(ColorBufferType::Luminance(luma as u8)) }, _ => None, } } } fn float_pixels(&self) -> bool { if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) { let render_type = unsafe { self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum }; render_type == glx_extra::RGBA_FLOAT_BIT_ARB } else { false } } fn alpha_size(&self) -> u8 { unsafe { self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 } } fn hardware_accelerated(&self) -> bool { unsafe { self.raw_attribute(glx::CONFIG_CAVEAT as c_int) != glx::SLOW_CONFIG as c_int } } fn srgb_capable(&self) -> bool { if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } } else { false } } fn depth_size(&self) -> u8 { unsafe { self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 } } fn stencil_size(&self) -> u8 { unsafe { self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 } } fn num_samples(&self) -> u8 { unsafe { self.raw_attribute(glx::SAMPLES as c_int) as u8 } } fn config_surface_types(&self) -> ConfigSurfaceTypes { let mut ty = ConfigSurfaceTypes::empty(); let raw_ty = unsafe { self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32 }; if raw_ty & glx::WINDOW_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::WINDOW); } if raw_ty & glx::PBUFFER_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::PBUFFER); } if raw_ty & glx::PIXMAP_BIT as u32 != 0 { ty.insert(ConfigSurfaceTypes::PIXMAP); } ty } fn supports_transparency(&self) -> Option { self.x11_visual().map(|visual| visual.supports_transparency()) } fn api(&self) -> Api { let mut api = Api::OPENGL; if self.inner.display.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT) { api |= Api::GLES1 | Api::GLES2; } api } } impl X11GlConfigExt for Config { fn x11_visual(&self) -> Option { unsafe { let raw_visual = self .inner .display .inner .glx .GetVisualFromFBConfig(self.inner.display.inner.raw.cast(), *self.inner.raw); if raw_visual.is_null() { None } else { Some(X11VisualInfo::from_raw( self.inner.display.inner.raw.cast(), raw_visual as *mut _, )) } } } } impl GetGlDisplay for Config { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawConfig for Config { fn raw_config(&self) -> RawConfig { RawConfig::Glx(*self.inner.raw) } } impl Sealed for Config {} pub(crate) struct ConfigInner { display: Display, pub(crate) raw: GlxConfig, } impl PartialEq for ConfigInner { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } impl Eq for ConfigInner {} impl fmt::Debug for ConfigInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Config") .field("raw", &self.raw) .field("display", &self.display.inner.raw) .finish() } } #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct GlxConfig(GLXFBConfig); unsafe impl Send for GlxConfig {} unsafe impl Sync for GlxConfig {} impl Deref for GlxConfig { type Target = GLXFBConfig; fn deref(&self) -> &Self::Target { &self.0 } } glutin-0.32.2/src/api/glx/context.rs000064400000000000000000000365021046102023000154040ustar 00000000000000//! Everything related to `GLXContext`. use std::fmt; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::c_int; use glutin_glx_sys::glx::types::GLXContext; use glutin_glx_sys::{glx, glx_extra}; use crate::config::GetGlConfig; use crate::context::{ self, AsRawContext, ContextApi, ContextAttributes, GlProfile, Priority, RawContext, ReleaseBehavior, Robustness, Version, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::SurfaceTypeTrait; use super::config::Config; use super::display::Display; use super::surface::Surface; impl Display { pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, ) -> Result { let shared_context = if let Some(shared_context) = context_attributes.shared_context.as_ref() { match shared_context { RawContext::Glx(shared_context) => *shared_context, #[allow(unreachable_patterns)] _ => return Err(ErrorKind::NotSupported("incompatible context was passed").into()), } } else { std::ptr::null() }; let (context, supports_surfaceless) = if self.inner.client_extensions.contains("GLX_ARB_create_context") && self.inner.glx_extra.is_some() { self.create_context_arb(config, context_attributes, shared_context)? } else { (self.create_context_legacy(config, shared_context)?, false) }; // Failed to create the context. if context.is_null() { return Err(ErrorKind::BadContext.into()); } let config = config.clone(); let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_))); let inner = ContextInner { display: self.clone(), config, raw: GlxContext(context), is_gles, supports_surfaceless, }; Ok(NotCurrentContext::new(inner)) } fn create_context_arb( &self, config: &Config, context_attributes: &ContextAttributes, shared_context: GLXContext, ) -> Result<(GLXContext, bool)> { let extra = self.inner.glx_extra.as_ref().unwrap(); let mut attrs = Vec::::with_capacity(16); // Check whether the ES context creation is supported. let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT); let (profile, version, supports_surfaceless) = match context_attributes.api { api @ Some(ContextApi::OpenGl(_)) | api @ None => { let version = api.and_then(|api| api.version()); let (profile, version) = context::pick_profile(context_attributes.profile, version); let profile = match profile { GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, }; // Surfaceless contexts are supported with the GLX_ARB_create_context extension // when using OpenGL 3.0 or greater. let supports_surfaceless = version >= Version::new(3, 0); (Some(profile), Some(version), supports_surfaceless) }, Some(ContextApi::Gles(version)) if supports_es => ( Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT), Some(version.unwrap_or(Version::new(2, 0))), false, ), _ => { return Err(ErrorKind::NotSupported( "extension to create ES context with glx is not present.", ) .into()) }, }; // Set the profile. if let Some(profile) = profile { attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attrs.push(profile as c_int); } // Add version. if let Some(version) = version { attrs.push(glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); attrs.push(version.major as c_int); attrs.push(glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int); attrs.push(version.minor as c_int); } if let Some(profile) = context_attributes.profile { let profile = match profile { GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, }; attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attrs.push(profile as c_int); } let mut flags: c_int = 0; let mut requested_no_error = false; if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) { match context_attributes.robustness { Robustness::NotRobust => (), Robustness::RobustNoResetNotification => { attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); attrs.push(glx_extra::NO_RESET_NOTIFICATION_ARB as c_int); flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; }, Robustness::RobustLoseContextOnReset => { attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); attrs.push(glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int); flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; }, Robustness::NoError => { if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) { return Err(ErrorKind::NotSupported( "GLX_ARB_create_context_no_error not supported", ) .into()); } attrs.push(glx_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int); attrs.push(1); requested_no_error = true; }, } } else if context_attributes.robustness != Robustness::NotRobust { return Err(ErrorKind::NotSupported( "GLX_ARB_create_context_robustness is not supported", ) .into()); } // Debug flag. if context_attributes.debug && !requested_no_error { flags |= glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int; } if flags != 0 { attrs.push(glx_extra::CONTEXT_FLAGS_ARB as c_int); attrs.push(flags as c_int); } // Flush control. if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) { match context_attributes.release_behavior { // This is the default behavior in specification. // // XXX passing it explicitly causing issues with older mesa versions. ReleaseBehavior::Flush => (), ReleaseBehavior::None => { attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int); }, } } else if context_attributes.release_behavior != ReleaseBehavior::Flush { return Err(ErrorKind::NotSupported( "flush control behavior GLX_ARB_context_flush_control", ) .into()); } // Terminate list with zero. attrs.push(0); let context = super::last_glx_error(|| unsafe { extra.CreateContextAttribsARB( self.inner.raw.cast(), *config.inner.raw, shared_context, // Direct context 1, attrs.as_ptr(), ) })?; Ok((context, supports_surfaceless)) } fn create_context_legacy( &self, config: &Config, shared_context: GLXContext, ) -> Result { let render_type = if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE }; super::last_glx_error(|| unsafe { self.inner.glx.CreateNewContext( self.inner.raw.cast(), *config.inner.raw, render_type as c_int, shared_context, // Direct context. 1, ) }) } } /// A wrapper around `GLXContext` that is known to be not current. #[derive(Debug)] pub struct NotCurrentContext { inner: ContextInner, } impl NotCurrentContext { /// Make a [`Self::PossiblyCurrentContext`] indicating that the context /// could be current on the thread. /// /// Requires the GLX_ARB_create_context extension and OpenGL 3.0 or greater. pub fn make_current_surfaceless(self) -> Result { self.inner.make_current_surfaceless()?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn new(inner: ContextInner) -> Self { Self { inner } } } impl NotCurrentGlContext for NotCurrentContext { type PossiblyCurrentContext = PossiblyCurrentContext; type Surface = Surface; fn treat_as_possibly_current(self) -> PossiblyCurrentContext { PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } } fn make_current( self, surface: &Self::Surface, ) -> Result { self.inner.make_current_draw_read(surface, surface)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn make_current_draw_read( self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result { self.inner.make_current_draw_read(surface_draw, surface_read)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } } impl GlContext for NotCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl GetGlConfig for NotCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for NotCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for NotCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Glx(*self.inner.raw) } } impl Sealed for NotCurrentContext {} /// A wrapper around `GLXContext` that could be current for the current thread. #[derive(Debug)] pub struct PossiblyCurrentContext { inner: ContextInner, // The context could be current only on the one thread. _nosendsync: PhantomData, } impl PossiblyCurrentContext { /// Make this context current on the calling thread. /// /// Requires the GLX_ARB_create_context extension and OpenGL 3.0 or greater. pub fn make_current_surfaceless(&self) -> Result<()> { self.inner.make_current_surfaceless() } } impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; type Surface = Surface; fn make_not_current(self) -> Result { self.make_not_current_in_place()?; Ok(NotCurrentContext::new(self.inner)) } fn make_not_current_in_place(&self) -> Result<()> { self.inner.make_not_current() } fn is_current(&self) -> bool { unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw } } fn make_current(&self, surface: &Self::Surface) -> Result<()> { self.inner.make_current_draw_read(surface, surface) } fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()> { self.inner.make_current_draw_read(surface_draw, surface_read) } } impl GlContext for PossiblyCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl GetGlConfig for PossiblyCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GetGlDisplay for PossiblyCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawContext for PossiblyCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Glx(*self.inner.raw) } } impl Sealed for PossiblyCurrentContext {} struct ContextInner { display: Display, config: Config, raw: GlxContext, is_gles: bool, supports_surfaceless: bool, } impl ContextInner { fn make_current_surfaceless(&self) -> Result<()> { if !self.supports_surfaceless { return Err( ErrorKind::NotSupported("the surfaceless context Api isn't supported").into() ); } // Passing zero arguments for both `draw` and `read` parameters makes // the context current without a default framebuffer. super::last_glx_error(|| unsafe { self.display.inner.glx.MakeContextCurrent( self.display.inner.raw.cast(), 0, 0, *self.raw, ); }) } fn make_current_draw_read( &self, surface_draw: &Surface, surface_read: &Surface, ) -> Result<()> { super::last_glx_error(|| unsafe { self.display.inner.glx.MakeContextCurrent( self.display.inner.raw.cast(), surface_draw.raw, surface_read.raw, *self.raw, ); }) } fn make_not_current(&self) -> Result<()> { super::last_glx_error(|| unsafe { self.display.inner.glx.MakeContextCurrent( self.display.inner.raw.cast(), 0, 0, std::ptr::null(), ); }) } fn context_api(&self) -> ContextApi { if self.is_gles { ContextApi::Gles(None) } else { ContextApi::OpenGl(None) } } } impl Drop for ContextInner { fn drop(&mut self) { let _ = super::last_glx_error(|| unsafe { self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), *self.raw); }); } } impl fmt::Debug for ContextInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") .field("display", &self.display.inner.raw) .field("config", &self.config.inner.raw) .field("raw", &self.raw) .finish() } } #[derive(Debug)] struct GlxContext(GLXContext); unsafe impl Send for GlxContext {} impl Deref for GlxContext { type Target = GLXContext; fn deref(&self) -> &Self::Target { &self.0 } } glutin-0.32.2/src/api/glx/display.rs000064400000000000000000000224241046102023000153630ustar 00000000000000//! GLX object creation. use std::collections::HashSet; use std::ffi::{self, CStr}; use std::fmt; use std::ops::Deref; use std::sync::atomic::Ordering; use std::sync::Arc; use glutin_glx_sys::glx; use glutin_glx_sys::glx::types::Display as GLXDisplay; use raw_window_handle::RawDisplayHandle; use crate::config::ConfigTemplate; use crate::context::Version; use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; use super::config::Config; use super::context::NotCurrentContext; use super::surface::Surface; use super::{Glx, GlxExtra, XlibErrorHookRegistrar, GLX, GLX_BASE_ERROR, GLX_EXTRA}; /// A wrapper for the `GLXDisplay`, which is basically an `XDisplay`. #[derive(Debug, Clone)] pub struct Display { pub(crate) inner: Arc, } impl Display { /// Create GLX display. /// /// # Safety /// /// The `display` must point to the valid Xlib display and /// `error_hook_registrar` must be registered in your Xlib error handling /// callback. pub unsafe fn new( display: RawDisplayHandle, error_hook_registrar: XlibErrorHookRegistrar, ) -> Result { // Don't load GLX when unsupported platform was requested. let (display, screen) = match display { RawDisplayHandle::Xlib(handle) => match handle.display { Some(display) => (GlxDisplay(display.as_ptr() as *mut _), handle.screen as i32), None => return Err(ErrorKind::BadDisplay.into()), }, _ => { return Err( ErrorKind::NotSupported("provided native display isn't supported").into() ) }, }; let glx = match GLX.as_ref() { Some(glx) => glx, None => return Err(ErrorKind::NotFound.into()), }; // Set the base for errors coming from GLX. unsafe { let mut error_base = 0; let mut event_base = 0; if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { // The glx extension isn't present. return Err(ErrorKind::InitializationFailed.into()); } GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); } // This is completely ridiculous, but VirtualBox's OpenGL driver needs // some call handled by *it* (i.e. not Mesa) to occur before // anything else can happen. That is because VirtualBox's OpenGL // driver is going to apply binary patches to Mesa in the DLL // constructor and until it's loaded it won't have a chance to do that. // // The easiest way to do this is to just call `glXQueryVersion()` before // doing anything else. See: https://www.virtualbox.org/ticket/8293 let version = unsafe { let (mut major, mut minor) = (0, 0); if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { return Err(ErrorKind::InitializationFailed.into()); } Version::new(major as u8, minor as u8) }; if version < Version::new(1, 3) { return Err(ErrorKind::NotSupported("the glx below 1.3 isn't supported").into()); } // Register the error handling hook. error_hook_registrar(Box::new(super::glx_error_hook)); let client_extensions = get_extensions(glx, display); let features = Self::extract_display_features(&client_extensions, version); let inner = Arc::new(DisplayInner { raw: display, glx, glx_extra: GLX_EXTRA.as_ref(), version, screen, features, client_extensions, }); Ok(Self { inner }) } /// Get a reference to the initialized GLX API. pub fn glx(&self) -> &'static Glx { self.inner.glx } fn extract_display_features( extensions: &HashSet<&'static str>, version: Version, ) -> DisplayFeatures { let mut features = DisplayFeatures::empty(); features.set( DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, version >= Version::new(1, 4) || extensions.contains("GLX_ARB_multisample"), ); features.set( DisplayFeatures::FLOAT_PIXEL_FORMAT, extensions.contains("GLX_ARB_fbconfig_float"), ); features.set( DisplayFeatures::SRGB_FRAMEBUFFERS, extensions.contains("GLX_ARB_framebuffer_sRGB") || extensions.contains("GLX_EXT_framebuffer_sRGB"), ); features.set( DisplayFeatures::CREATE_ES_CONTEXT, extensions.contains("GLX_EXT_create_context_es2_profile") || extensions.contains("GLX_EXT_create_context_es_profile"), ); features.set( DisplayFeatures::SWAP_CONTROL, extensions.contains("GLX_EXT_swap_control") || extensions.contains("GLX_SGI_swap_control") || extensions.contains("GLX_MESA_swap_control"), ); features.set( DisplayFeatures::CONTEXT_ROBUSTNESS, extensions.contains("GLX_ARB_create_context_robustness"), ); features.set( DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, extensions.contains("GLX_ARB_context_flush_control"), ); features.set( DisplayFeatures::CONTEXT_NO_ERROR, extensions.contains("GLX_ARB_create_context_no_error"), ); features } } impl GlDisplay for Display { type Config = Config; type NotCurrentContext = NotCurrentContext; type PbufferSurface = Surface; type PixmapSurface = Surface; type WindowSurface = Surface; unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( &self, config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { unsafe { self.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ } } fn version_string(&self) -> String { format!("GLX {}.{}", self.inner.version.major, self.inner.version.minor) } fn supported_features(&self) -> DisplayFeatures { self.inner.features } } impl GetDisplayExtensions for Display { fn extensions(&self) -> &HashSet<&'static str> { &self.inner.client_extensions } } impl AsRawDisplay for Display { fn raw_display(&self) -> RawDisplay { RawDisplay::Glx(self.inner.raw.cast()) } } impl Sealed for Display {} pub(crate) struct DisplayInner { pub(crate) glx: &'static Glx, pub(crate) glx_extra: Option<&'static GlxExtra>, pub(crate) raw: GlxDisplay, pub(crate) screen: i32, pub(crate) version: Version, pub(crate) features: DisplayFeatures, /// Client GLX extensions. pub(crate) client_extensions: HashSet<&'static str>, } impl fmt::Debug for DisplayInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Display") .field("raw", &self.raw) .field("version", &self.version) .field("screen", &self.screen) .field("features", &self.features) .field("extensions", &self.client_extensions) .finish() } } #[derive(Debug, Clone, Copy)] pub(crate) struct GlxDisplay(*mut GLXDisplay); unsafe impl Send for GlxDisplay {} unsafe impl Sync for GlxDisplay {} impl Deref for GlxDisplay { type Target = *mut GLXDisplay; fn deref(&self) -> &Self::Target { &self.0 } } /// Load the GLX extensions. fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> { unsafe { let extensions = glx.GetClientString(display.0, glx::EXTENSIONS as i32); if extensions.is_null() { return HashSet::new(); } if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { extensions.split(' ').collect::>() } else { HashSet::new() } } } glutin-0.32.2/src/api/glx/mod.rs000064400000000000000000000154371046102023000145030ustar 00000000000000//! GLX platform Api. #![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support use std::ffi::{self, CStr, CString}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use std::sync::Mutex; use libloading::Library; use once_cell::sync::Lazy; use x11_dl::xlib::{self, XErrorEvent}; use glutin_glx_sys::{glx, glx_extra}; use crate::error::{Error, ErrorKind, Result}; use crate::lib_loading::{SymLoading, SymWrapper}; use crate::platform::x11::XLIB; pub mod config; pub mod context; pub mod display; pub mod surface; /// When using Xlib we need to get errors from it somehow, however creating /// inner `XDisplay` to handle that or change the error hook is unsafe in /// multithreaded applications, given that error hook is per process and not /// connection. /// /// The hook registrar must call to the function inside xlib error /// [`handler`]. /// /// The `bool` value returned by that hook tells whether the error was handled /// by it or not. So when it returns `true` it means that your error handling /// routine shouldn't handle the error as it was handled by the hook already. /// /// [`handler`]: https://tronche.com/gui/x/xlib/event-handling/protocol-errors/XSetErrorHandler.html pub type XlibErrorHookRegistrar = Box bool + Send + Sync>)>; /// The base used for GLX errors. static GLX_BASE_ERROR: AtomicI32 = AtomicI32::new(0); /// The last error arrived from GLX normalized by `GLX_BASE_ERROR`. static LAST_GLX_ERROR: Lazy>> = Lazy::new(|| Mutex::new(None)); /// Whether we're in the process of getting GLX error. Otherwise we may handle /// the winit's error. static SYNCING_GLX_ERROR: AtomicBool = AtomicBool::new(false); static GLX: Lazy> = Lazy::new(|| { let paths = ["libGL.so.1", "libGL.so"]; unsafe { SymWrapper::new(&paths).map(Glx).ok() } }); static GLX_EXTRA: Lazy> = Lazy::new(|| { let glx = GLX.as_ref()?; Some(GlxExtra::new(glx)) }); /// GLX interface. #[allow(missing_debug_implementations)] pub struct Glx(pub SymWrapper); unsafe impl Sync for Glx {} unsafe impl Send for Glx {} impl SymLoading for glx::Glx { unsafe fn load_with(lib: &Library) -> Self { Self::load_with(|sym| unsafe { lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) .map(|sym| *sym) .unwrap_or(std::ptr::null_mut()) }) } } impl Deref for Glx { type Target = glx::Glx; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Glx { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } pub(crate) struct GlxExtra(glx_extra::Glx); unsafe impl Sync for GlxExtra {} unsafe impl Send for GlxExtra {} impl GlxExtra { #[inline] pub fn new(glx: &Glx) -> Self { GlxExtra(glx_extra::Glx::load_with(|proc_name| { let c_str = CString::new(proc_name).unwrap(); unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } })) } } impl Deref for GlxExtra { type Target = glx_extra::Glx; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for GlxExtra { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } /// Store the last error received from the GLX. fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool { // In case we've not forced the sync, ignore the error. if !SYNCING_GLX_ERROR.load(Ordering::Relaxed) { return false; } let xerror = xerror_event as *mut XErrorEvent; unsafe { let code = (*xerror).error_code; let glx_code = code as i32 - GLX_BASE_ERROR.load(Ordering::Relaxed); // Get the kind of the error. let kind = match code as u8 { xlib::BadValue => ErrorKind::BadAttribute, xlib::BadMatch => ErrorKind::BadMatch, xlib::BadWindow => ErrorKind::BadNativeWindow, xlib::BadAlloc => ErrorKind::OutOfMemory, xlib::BadPixmap => ErrorKind::BadPixmap, xlib::BadAccess => ErrorKind::BadAccess, _ if glx_code >= 0 => match glx_code as glx::types::GLenum { glx::PROTO_BAD_CONTEXT => ErrorKind::BadContext, glx::PROTO_BAD_CONTEXT_STATE => ErrorKind::BadContext, glx::PROTO_BAD_CURRENT_DRAWABLE => ErrorKind::BadCurrentSurface, glx::PROTO_BAD_CURRENT_WINDOW => ErrorKind::BadCurrentSurface, glx::PROTO_BAD_FBCONFIG => ErrorKind::BadConfig, glx::PROTO_BAD_PBUFFER => ErrorKind::BadPbuffer, glx::PROTO_BAD_PIXMAP => ErrorKind::BadPixmap, glx::PROTO_UNSUPPORTED_PRIVATE_REQUEST => ErrorKind::Misc, glx::PROTO_BAD_DRAWABLE => ErrorKind::BadSurface, glx::PROTO_BAD_WINDOW => ErrorKind::BadSurface, glx::PROTO_BAD_CONTEXT_TAG => ErrorKind::Misc, glx::PROTO_BAD_RENDER_REQUEST => ErrorKind::Misc, glx::PROTO_BAD_LARGE_REQUEST => ErrorKind::Misc, _ => return false, }, _ => return false, }; // Get the string from X11 error. let mut buf = vec![0u8; 1024]; (XLIB.as_ref().unwrap().XGetErrorText)( _display as *mut _, (*xerror).error_code as _, buf.as_mut_ptr() as *mut _, buf.len() as _, ); let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string(); *LAST_GLX_ERROR.lock().unwrap() = Some(Error::new(Some(code as _), Some(description), kind)); true } } /// Prevent error being overwritten when accessing the handler from the multiple /// threads. static ERROR_SECTION_LOCK: Mutex<()> = Mutex::new(()); /// Get the error from the X11. /// /// XXX mesa and I'd guess other GLX implementations, send the error, by taking /// the Xlib Error handling hook, getting the current hook, and calling back to /// the user, meaning that no `XSync` should be done. fn last_glx_error T>(callback: F) -> Result { let _guard = ERROR_SECTION_LOCK.lock().unwrap(); // Mark that we're syncing the error. SYNCING_GLX_ERROR.store(true, Ordering::Relaxed); // Execute the user routine that may produce GLX error. let result = callback(); // XXX We might want to XSync here in addition, because what mesa is doing might // not be common, but I'd assume that what mesa doing is common. // Reset and report last error. let result = match LAST_GLX_ERROR.lock().unwrap().take() { Some(error) => Err(error), None => Ok(result), }; // Release the mark. SYNCING_GLX_ERROR.store(false, Ordering::Relaxed); result } glutin-0.32.2/src/api/glx/surface.rs000064400000000000000000000240651046102023000153510ustar 00000000000000//! Everything related to the GLXWindow. use std::fmt; use std::marker::PhantomData; use std::num::NonZeroU32; use std::os::raw::{c_int, c_uint}; use glutin_glx_sys::glx::types::GLXWindow; use glutin_glx_sys::{glx, glx_extra}; use raw_window_handle::RawWindowHandle; use crate::config::GetGlConfig; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::private::Sealed; use crate::surface::{ AsRawSurface, GlSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, SurfaceType, SurfaceTypeTrait, SwapInterval, WindowSurface, }; use super::config::Config; use super::context::PossiblyCurrentContext; use super::display::Display; /// Hint for the attributes array. const ATTR_SIZE_HINT: usize = 8; impl Display { pub(crate) unsafe fn create_pixmap_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); let xid = match native_pixmap { NativePixmap::XlibPixmap(xid) => { if *xid == 0 { return Err(ErrorKind::BadNativePixmap.into()); } *xid }, _ => { return Err( ErrorKind::NotSupported("provided native pixmap is not supported.").into() ) }, }; let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); // Push X11 `None` to terminate the list. attrs.push(0); let config = config.clone(); let surface = super::last_glx_error(|| unsafe { self.inner.glx.CreatePixmap( self.inner.raw.cast(), *config.inner.raw, xid, attrs.as_ptr(), ) })?; Ok(Surface { display: self.clone(), config, raw: surface, _nosendsync: PhantomData, _ty: PhantomData, }) } pub(crate) unsafe fn create_pbuffer_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let width = surface_attributes.width.unwrap(); let height = surface_attributes.height.unwrap(); let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); attrs.push(glx::PBUFFER_WIDTH as c_int); attrs.push(width.get() as c_int); attrs.push(glx::PBUFFER_HEIGHT as c_int); attrs.push(height.get() as c_int); attrs.push(glx::LARGEST_PBUFFER as c_int); attrs.push(surface_attributes.largest_pbuffer as c_int); // Push X11 `None` to terminate the list. attrs.push(0); let config = config.clone(); let surface = super::last_glx_error(|| unsafe { self.inner.glx.CreatePbuffer(self.inner.raw.cast(), *config.inner.raw, attrs.as_ptr()) })?; Ok(Surface { display: self.clone(), config, raw: surface, _nosendsync: PhantomData, _ty: PhantomData, }) } pub(crate) unsafe fn create_window_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let window = match surface_attributes.raw_window_handle.unwrap() { RawWindowHandle::Xlib(window_handle) => { if window_handle.window == 0 { return Err(ErrorKind::BadNativeWindow.into()); } window_handle.window }, _ => { return Err( ErrorKind::NotSupported("provided native window is not supported").into() ) }, }; let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); // Push X11 `None` to terminate the list. attrs.push(0); let config = config.clone(); let surface = super::last_glx_error(|| unsafe { self.inner.glx.CreateWindow( self.inner.raw.cast(), *config.inner.raw, window, attrs.as_ptr() as *const _, ) })?; Ok(Surface { display: self.clone(), config, raw: surface, _nosendsync: PhantomData, _ty: PhantomData, }) } } /// A wrapper around the `GLXWindow`. pub struct Surface { display: Display, config: Config, pub(crate) raw: GLXWindow, _nosendsync: PhantomData<*const std::ffi::c_void>, _ty: PhantomData, } // Impl only `Send` for Surface. unsafe impl Send for Surface {} impl Surface { /// # Safety /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: c_int) -> c_uint { unsafe { let mut value = 0; // This shouldn't generate any errors given that we know that the surface is // valid. self.display.inner.glx.QueryDrawable( self.display.inner.raw.cast(), self.raw, attr, &mut value, ); value } } } impl Drop for Surface { fn drop(&mut self) { let _ = super::last_glx_error(|| unsafe { match T::surface_type() { SurfaceType::Pbuffer => { self.display.inner.glx.DestroyPbuffer(self.display.inner.raw.cast(), self.raw); }, SurfaceType::Window => { self.display.inner.glx.DestroyWindow(self.display.inner.raw.cast(), self.raw); }, SurfaceType::Pixmap => { self.display.inner.glx.DestroyPixmap(self.display.inner.raw.cast(), self.raw); }, } }); } } impl GlSurface for Surface { type Context = PossiblyCurrentContext; type SurfaceType = T; fn buffer_age(&self) -> u32 { self.display .inner .client_extensions .contains("GLX_EXT_buffer_age") .then(|| unsafe { self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) }) .unwrap_or(0) as u32 } fn width(&self) -> Option { unsafe { Some(self.raw_attribute(glx::WIDTH as c_int) as u32) } } fn height(&self) -> Option { unsafe { Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) } } fn is_single_buffered(&self) -> bool { self.config.is_single_buffered() } fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { super::last_glx_error(|| unsafe { self.display.inner.glx.SwapBuffers(self.display.inner.raw.cast(), self.raw); }) } fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { let extra = match self.display.inner.glx_extra { Some(extra) if self.display.inner.features.contains(DisplayFeatures::SWAP_CONTROL) => { extra }, _ => { return Err( ErrorKind::NotSupported("swap control extensions are not supported").into() ); }, }; let interval = match interval { SwapInterval::DontWait => 0, SwapInterval::Wait(n) => n.get(), }; let mut applied = false; // Apply the `EXT` first since it's per window. if !applied && self.display.inner.client_extensions.contains("GLX_EXT_swap_control") { super::last_glx_error(|| unsafe { // Check for error explicitly here, other apis do have indication for failure. extra.SwapIntervalEXT(self.display.inner.raw.cast(), self.raw, interval as _); applied = true; })?; } if !applied && self.display.inner.client_extensions.contains("GLX_MESA_swap_control") { unsafe { applied = extra.SwapIntervalMESA(interval as _) != glx::BAD_CONTEXT as _; } } if !applied && self.display.inner.client_extensions.contains("GLX_SGI_swap_control") { unsafe { applied = extra.SwapIntervalSGI(interval as _) != glx::BAD_CONTEXT as _; } } if applied { Ok(()) } else { Err(ErrorKind::BadContext.into()) } } fn is_current(&self, context: &Self::Context) -> bool { self.is_current_draw(context) && self.is_current_read(context) } fn is_current_draw(&self, _context: &Self::Context) -> bool { unsafe { self.display.inner.glx.GetCurrentDrawable() == self.raw } } fn is_current_read(&self, _context: &Self::Context) -> bool { unsafe { self.display.inner.glx.GetCurrentReadDrawable() == self.raw } } fn resize(&self, _context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { // This isn't supported with GLXDrawable. } } impl GetGlConfig for Surface { type Target = Config; fn config(&self) -> Self::Target { self.config.clone() } } impl GetGlDisplay for Surface { type Target = Display; fn display(&self) -> Self::Target { self.display.clone() } } impl fmt::Debug for Surface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Surface") .field("display", &self.display.inner.raw) .field("config", &self.config.inner.raw) .field("raw", &self.raw) .field("type", &T::surface_type()) .finish() } } impl AsRawSurface for Surface { fn raw_surface(&self) -> RawSurface { RawSurface::Glx(self.raw as u64) } } impl Sealed for Surface {} glutin-0.32.2/src/api/mod.rs000064400000000000000000000002551046102023000137010ustar 00000000000000//! The underlying OpenGL platform Api. #[cfg(cgl_backend)] pub mod cgl; #[cfg(egl_backend)] pub mod egl; #[cfg(glx_backend)] pub mod glx; #[cfg(wgl_backend)] pub mod wgl; glutin-0.32.2/src/api/wgl/config.rs000064400000000000000000000437741046102023000151750ustar 00000000000000//! Handling of PIXELFORMATDESCRIPTOR and pixel format index. use std::io::Error as IoError; use std::mem::{self, MaybeUninit}; use std::os::raw::c_int; use std::sync::Arc; use std::{fmt, iter}; use glutin_wgl_sys::wgl_extra; use raw_window_handle::RawWindowHandle; use windows_sys::Win32::Graphics::Gdi::{self as gdi, HDC}; use windows_sys::Win32::Graphics::OpenGL::{self as gl, PIXELFORMATDESCRIPTOR}; use crate::config::{ Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::private::Sealed; use super::display::Display; /// The maximum amount of configs to query. const MAX_QUERY_CONFIGS: usize = 256; // Srgb extensions. const SRGB_ARB: &str = "WGL_ARB_framebuffer_sRGB"; const SRGB_EXT: &str = "WGL_EXT_framebuffer_sRGB"; impl Display { pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { let hwnd = match template.native_window { Some(RawWindowHandle::Win32(window_handle)) => window_handle.hwnd.get() as _, _ => 0, }; let hdc = unsafe { gdi::GetDC(hwnd) }; match self.inner.wgl_extra { // Check that particular function was loaded. Some(wgl_extra) if wgl_extra.ChoosePixelFormatARB.is_loaded() => { self.find_configs_arb(template, hdc) }, _ => self.find_normal_configs(template, hdc), } } fn find_normal_configs( &self, template: ConfigTemplate, hdc: HDC, ) -> Result + '_>> { let (r_size, g_size, b_size) = match template.color_buffer_type { ColorBufferType::Rgb { r_size, g_size, b_size } => (r_size, g_size, b_size), _ => { return Err( ErrorKind::NotSupported("luminance buffers are not supported with WGL").into() ) }, }; let mut dw_flags = gl::PFD_SUPPORT_OPENGL; if !template.single_buffering { dw_flags |= gl::PFD_DOUBLEBUFFER; } if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { dw_flags |= gl::PFD_DRAW_TO_WINDOW; } if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { dw_flags |= gl::PFD_DRAW_TO_BITMAP; } dw_flags |= match template.stereoscopy { Some(true) => gl::PFD_STEREO, Some(false) => 0, None => gl::PFD_STEREO_DONTCARE, }; // Hardware acceleration. dw_flags |= match template.hardware_accelerated { Some(true) => gl::PFD_GENERIC_ACCELERATED, Some(false) => gl::PFD_GENERIC_FORMAT, None => 0, }; let pixel_format_descriptor = PIXELFORMATDESCRIPTOR { nSize: mem::size_of::() as _, // Should be one according to the docs. nVersion: 1, dwFlags: dw_flags, iPixelType: gl::PFD_TYPE_RGBA, cColorBits: r_size + g_size + b_size, cRedBits: r_size, cRedShift: 0, cGreenBits: g_size, cGreenShift: 0, cBlueBits: b_size, cBlueShift: 0, cAlphaBits: template.alpha_size, cAlphaShift: 0, cAccumBits: 0, cAccumRedBits: 0, cAccumGreenBits: 0, cAccumBlueBits: 0, cAccumAlphaBits: 0, cDepthBits: template.depth_size, cStencilBits: template.stencil_size, cAuxBuffers: 0, iLayerType: gl::PFD_MAIN_PLANE as u8, bReserved: 0, dwLayerMask: 0, dwVisibleMask: 0, dwDamageMask: 0, }; unsafe { let pixel_format_index = gl::ChoosePixelFormat(hdc, &pixel_format_descriptor); if pixel_format_index == 0 { return Err(ErrorKind::BadConfig.into()); } let mut descriptor = MaybeUninit::::uninit(); if gl::DescribePixelFormat( hdc, pixel_format_index as _, mem::size_of::() as _, descriptor.as_mut_ptr(), ) == 0 { return Err(IoError::last_os_error().into()); }; let descriptor = descriptor.assume_init(); if descriptor.iPixelType != gl::PFD_TYPE_RGBA { return Err(ErrorKind::BadConfig.into()); } let inner = Arc::new(ConfigInner { display: self.clone(), hdc, pixel_format_index, descriptor: Some(descriptor), }); let config = Config { inner }; Ok(Box::new(iter::once(config))) } } fn find_configs_arb( &self, template: ConfigTemplate, hdc: HDC, ) -> Result + '_>> { let wgl_extra = self.inner.wgl_extra.unwrap(); let mut attrs = Vec::::with_capacity(32); match template.color_buffer_type { ColorBufferType::Rgb { r_size, g_size, b_size } => { attrs.push(wgl_extra::RED_BITS_ARB as c_int); attrs.push(r_size as c_int); attrs.push(wgl_extra::GREEN_BITS_ARB as c_int); attrs.push(g_size as c_int); attrs.push(wgl_extra::BLUE_BITS_ARB as c_int); attrs.push(b_size as c_int); }, _ => { return Err( ErrorKind::NotSupported("luminance buffers are not supported with WGL").into() ) }, } attrs.push(wgl_extra::ALPHA_BITS_ARB as c_int); attrs.push(template.alpha_size as c_int); attrs.push(wgl_extra::DEPTH_BITS_ARB as c_int); attrs.push(template.depth_size as c_int); attrs.push(wgl_extra::STENCIL_BITS_ARB as c_int); attrs.push(template.stencil_size as c_int); attrs.push(wgl_extra::SUPPORT_OPENGL_ARB as c_int); attrs.push(1); attrs.push(wgl_extra::DOUBLE_BUFFER_ARB as c_int); attrs.push(!template.single_buffering as c_int); let pixel_type = if self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) && template.float_pixels { wgl_extra::TYPE_RGBA_FLOAT_ARB } else if template.float_pixels { return Err(ErrorKind::NotSupported("float pixels are not supported").into()); } else { wgl_extra::TYPE_RGBA_ARB }; if let Some(num_samples) = template.num_samples { if self.inner.features.contains(DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS) { attrs.push(wgl_extra::SAMPLE_BUFFERS_ARB as c_int); attrs.push(1); attrs.push(wgl_extra::SAMPLES_ARB as c_int); attrs.push(num_samples as c_int); } } attrs.push(wgl_extra::PIXEL_TYPE_ARB as c_int); attrs.push(pixel_type as c_int); if let Some(stereo) = template.stereoscopy { attrs.push(wgl_extra::STEREO_ARB as c_int); attrs.push(stereo as c_int) } if let Some(hardware_accelerated) = template.hardware_accelerated { attrs.push(wgl_extra::ACCELERATION_ARB as c_int); if hardware_accelerated { attrs.push(wgl_extra::FULL_ACCELERATION_ARB as c_int); } else { attrs.push(wgl_extra::NO_ACCELERATION_ARB as c_int); } } if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) { attrs.push(wgl_extra::DRAW_TO_WINDOW_ARB as c_int); attrs.push(1); } if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) { attrs.push(wgl_extra::DRAW_TO_WINDOW_ARB as c_int); attrs.push(1); } if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) { attrs.push(wgl_extra::DRAW_TO_PBUFFER_ARB as c_int); attrs.push(1); } if template.transparency { attrs.push(wgl_extra::TRANSPARENT_ARB as c_int); attrs.push(1); } // Terminate attrs with zero. attrs.push(0); unsafe { let mut num_configs = 0; let mut configs = Vec::::with_capacity(MAX_QUERY_CONFIGS); if wgl_extra.ChoosePixelFormatARB( hdc as *const _, attrs.as_ptr().cast(), std::ptr::null(), configs.capacity() as _, configs.as_mut_ptr().cast(), &mut num_configs, ) == 0 { return Err(IoError::last_os_error().into()); } configs.set_len(num_configs as _); Ok(Box::new(configs.into_iter().map(move |pixel_format_index| { let inner = Arc::new(ConfigInner { display: self.clone(), hdc, pixel_format_index, descriptor: None, }); Config { inner } }))) } } } /// A wrapper around `PIXELFORMAT`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { pub(crate) inner: Arc, } impl Config { /// Set the pixel format on the native window. /// /// # Safety /// /// The `raw_window_handle` should point to a valid value. pub unsafe fn apply_on_native_window(&self, raw_window_handle: &RawWindowHandle) -> Result<()> { let hdc = match raw_window_handle { RawWindowHandle::Win32(window) => unsafe { gdi::GetDC(window.hwnd.get() as _) }, _ => return Err(ErrorKind::BadNativeWindow.into()), }; let descriptor = self.inner.descriptor.as_ref().map(|desc| desc as _).unwrap_or(std::ptr::null()); unsafe { if gl::SetPixelFormat(hdc, self.inner.pixel_format_index, descriptor) == 0 { Err(IoError::last_os_error().into()) } else { Ok(()) } } } pub(crate) fn is_single_buffered(&self) -> bool { match self.inner.descriptor.as_ref() { Some(descriptor) => (descriptor.dwFlags & gl::PFD_DOUBLEBUFFER) == 0, None => unsafe { self.raw_attribute(wgl_extra::DOUBLE_BUFFER_ARB as c_int) == 0 }, } } /// # Safety /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: c_int) -> c_int { unsafe { let wgl_extra = self.inner.display.inner.wgl_extra.unwrap(); let mut res = 0; wgl_extra.GetPixelFormatAttribivARB( self.inner.hdc as *const _, self.inner.pixel_format_index, gl::PFD_MAIN_PLANE as _, 1, &attr, &mut res, ); res } } } impl GlConfig for Config { fn color_buffer_type(&self) -> Option { let (r_size, g_size, b_size) = match self.inner.descriptor.as_ref() { Some(descriptor) => (descriptor.cRedBits, descriptor.cGreenBits, descriptor.cBlueBits), _ => unsafe { let r_size = self.raw_attribute(wgl_extra::RED_BITS_ARB as c_int) as u8; let g_size = self.raw_attribute(wgl_extra::GREEN_BITS_ARB as c_int) as u8; let b_size = self.raw_attribute(wgl_extra::BLUE_BITS_ARB as c_int) as u8; (r_size, g_size, b_size) }, }; Some(ColorBufferType::Rgb { r_size, g_size, b_size }) } fn float_pixels(&self) -> bool { unsafe { self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) && self.raw_attribute(wgl_extra::PIXEL_TYPE_ARB as c_int) == wgl_extra::TYPE_RGBA_FLOAT_ARB as c_int } } fn alpha_size(&self) -> u8 { match self.inner.descriptor.as_ref() { Some(descriptor) => descriptor.cAlphaBits, _ => unsafe { self.raw_attribute(wgl_extra::ALPHA_BITS_ARB as c_int) as _ }, } } fn srgb_capable(&self) -> bool { if self.inner.display.inner.client_extensions.contains(SRGB_EXT) || self.inner.display.inner.client_extensions.contains("WGL_EXT_colorspace") { unsafe { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } } else if self.inner.display.inner.client_extensions.contains(SRGB_ARB) { unsafe { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } } else { false } } fn depth_size(&self) -> u8 { match self.inner.descriptor.as_ref() { Some(descriptor) => descriptor.cDepthBits, _ => unsafe { self.raw_attribute(wgl_extra::DEPTH_BITS_ARB as c_int) as _ }, } } fn stencil_size(&self) -> u8 { match self.inner.descriptor.as_ref() { Some(descriptor) => descriptor.cStencilBits, _ => unsafe { self.raw_attribute(wgl_extra::STENCIL_BITS_ARB as c_int) as _ }, } } fn num_samples(&self) -> u8 { if self.inner.display.inner.features.contains(DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS) { unsafe { self.raw_attribute(wgl_extra::SAMPLES_ARB as c_int) as _ } } else { 0 } } fn config_surface_types(&self) -> ConfigSurfaceTypes { let mut flags = ConfigSurfaceTypes::empty(); match self.inner.descriptor.as_ref() { Some(descriptor) => { let dw_flags = descriptor.dwFlags; if dw_flags & gl::PFD_DRAW_TO_WINDOW != 0 { flags |= ConfigSurfaceTypes::WINDOW; } if dw_flags & gl::PFD_DRAW_TO_BITMAP != 0 { flags |= ConfigSurfaceTypes::PIXMAP; } }, _ => unsafe { if self.raw_attribute(wgl_extra::DRAW_TO_WINDOW_ARB as c_int) != 0 { flags |= ConfigSurfaceTypes::WINDOW } if self.raw_attribute(wgl_extra::DRAW_TO_BITMAP_ARB as c_int) != 0 { flags |= ConfigSurfaceTypes::WINDOW } }, } flags } fn hardware_accelerated(&self) -> bool { if let Some(descriptor) = self.inner.descriptor.as_ref() { descriptor.dwFlags & gl::PFD_GENERIC_ACCELERATED != 0 } else { unsafe { self.raw_attribute(wgl_extra::ACCELERATION_ARB as c_int) != wgl_extra::NO_ACCELERATION_ARB as c_int } } } fn supports_transparency(&self) -> Option { if self.inner.descriptor.as_ref().is_some() { None } else { unsafe { Some(self.raw_attribute(wgl_extra::TRANSPARENT_ARB as c_int) != 0) } } } fn api(&self) -> Api { let mut api = Api::OPENGL; if self.inner.display.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT) { api |= Api::GLES1 | Api::GLES2; } api } } impl GetGlDisplay for Config { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl AsRawConfig for Config { fn raw_config(&self) -> RawConfig { RawConfig::Wgl(self.inner.pixel_format_index) } } impl Sealed for Config {} pub(crate) struct ConfigInner { pub(crate) display: Display, pub(crate) hdc: HDC, pub(crate) pixel_format_index: i32, pub(crate) descriptor: Option, } impl PartialEq for ConfigInner { fn eq(&self, other: &Self) -> bool { self.pixel_format_index == other.pixel_format_index } } impl Eq for ConfigInner {} impl fmt::Debug for ConfigInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Config") .field("hdc", &self.hdc) .field("pixel_format_index", &self.pixel_format_index) .finish() } } /// This function chooses a pixel format that is likely to be provided by the /// main video driver of the system. pub(crate) fn choose_dummy_pixel_format(hdc: HDC) -> Result<(i32, PIXELFORMATDESCRIPTOR)> { let descriptor = PIXELFORMATDESCRIPTOR { nSize: std::mem::size_of::() as u16, nVersion: 1, dwFlags: gl::PFD_DRAW_TO_WINDOW | gl::PFD_SUPPORT_OPENGL | gl::PFD_DOUBLEBUFFER, iPixelType: gl::PFD_TYPE_RGBA, cColorBits: 24, cRedBits: 0, cRedShift: 0, cGreenBits: 0, cGreenShift: 0, cBlueBits: 0, cBlueShift: 0, cAlphaBits: 8, cAlphaShift: 0, cAccumBits: 0, cAccumRedBits: 0, cAccumGreenBits: 0, cAccumBlueBits: 0, cAccumAlphaBits: 0, cDepthBits: 24, cStencilBits: 8, cAuxBuffers: 0, iLayerType: gl::PFD_MAIN_PLANE as u8, bReserved: 0, dwLayerMask: 0, dwVisibleMask: 0, dwDamageMask: 0, }; let pixel_format_index = unsafe { gl::ChoosePixelFormat(hdc, &descriptor) }; if pixel_format_index == 0 { return Err(IoError::last_os_error().into()); } unsafe { let mut descriptor = MaybeUninit::::uninit(); if gl::DescribePixelFormat( hdc, pixel_format_index as _, mem::size_of::() as _, descriptor.as_mut_ptr(), ) == 0 { return Err(IoError::last_os_error().into()); }; let descriptor = descriptor.assume_init(); if descriptor.iPixelType != gl::PFD_TYPE_RGBA { return Err(IoError::last_os_error().into()); } Ok((pixel_format_index, descriptor)) } } glutin-0.32.2/src/api/wgl/context.rs000064400000000000000000000354061046102023000154050ustar 00000000000000//! WGL context handling. use std::fmt; use std::io::Error as IoError; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::c_int; use glutin_wgl_sys::wgl::types::HGLRC; use glutin_wgl_sys::{wgl, wgl_extra}; use raw_window_handle::RawWindowHandle; use windows_sys::Win32::Graphics::Gdi::{self as gdi, HDC}; use crate::config::GetGlConfig; use crate::context::{ self, AsRawContext, ContextApi, ContextAttributes, GlProfile, Priority, RawContext, ReleaseBehavior, Robustness, Version, }; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::SurfaceTypeTrait; use super::config::Config; use super::display::Display; use super::surface::Surface; impl Display { pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, ) -> Result { let hdc = match context_attributes.raw_window_handle.as_ref() { handle @ Some(RawWindowHandle::Win32(window)) => unsafe { let _ = config.apply_on_native_window(handle.unwrap()); gdi::GetDC(window.hwnd.get() as _) }, _ => config.inner.hdc, }; let share_ctx = match context_attributes.shared_context { Some(RawContext::Wgl(share)) => share, _ => std::ptr::null(), }; let (context, supports_surfaceless) = if self.inner.client_extensions.contains("WGL_ARB_create_context") { self.create_context_arb(hdc, share_ctx, context_attributes)? } else { unsafe { let raw = wgl::CreateContext(hdc as *const _); if raw.is_null() { return Err(IoError::last_os_error().into()); } // Context sharing. if !share_ctx.is_null() && wgl::ShareLists(share_ctx, raw) == 0 { return Err(IoError::last_os_error().into()); } (WglContext(raw), false) } }; let config = config.clone(); let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_))); let inner = ContextInner { display: self.clone(), config, raw: context, is_gles, supports_surfaceless, }; Ok(NotCurrentContext { inner }) } fn create_context_arb( &self, hdc: HDC, share_context: HGLRC, context_attributes: &ContextAttributes, ) -> Result<(WglContext, bool)> { let extra = self.inner.wgl_extra.as_ref().unwrap(); let mut attrs = Vec::::with_capacity(16); // Check whether the ES context creation is supported. let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT); let (profile, version, supports_surfaceless) = match context_attributes.api { api @ Some(ContextApi::OpenGl(_)) | api @ None => { let version = api.and_then(|api| api.version()); let (profile, version) = context::pick_profile(context_attributes.profile, version); let profile = match profile { GlProfile::Core => wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, GlProfile::Compatibility => wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, }; // Surfaceless contexts are supported with the WGL_ARB_create_context extension // when using OpenGL 3.0 or greater. let supports_surfaceless = version >= Version::new(3, 0); (Some(profile), Some(version), supports_surfaceless) }, Some(ContextApi::Gles(version)) if supports_es => ( Some(wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT), Some(version.unwrap_or(Version::new(2, 0))), false, ), _ => { return Err(ErrorKind::NotSupported( "extension to create ES context with wgl is not present", ) .into()) }, }; // Set the profile. if let Some(profile) = profile { attrs.push(wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attrs.push(profile as c_int); } // Add version. if let Some(version) = version { attrs.push(wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); attrs.push(version.major as c_int); attrs.push(wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int); attrs.push(version.minor as c_int); } if let Some(profile) = context_attributes.profile { let profile = match profile { GlProfile::Core => wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, GlProfile::Compatibility => wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, }; attrs.push(wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attrs.push(profile as c_int); } let mut flags: c_int = 0; let mut requested_no_error = false; if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) { match context_attributes.robustness { Robustness::NotRobust => (), Robustness::RobustNoResetNotification => { attrs.push(wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); attrs.push(wgl_extra::NO_RESET_NOTIFICATION_ARB as c_int); flags |= wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; }, Robustness::RobustLoseContextOnReset => { attrs.push(wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); attrs.push(wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int); flags |= wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; }, Robustness::NoError => { if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) { return Err(ErrorKind::NotSupported( "WGL_ARB_create_context_no_error not supported", ) .into()); } attrs.push(wgl_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int); attrs.push(1); requested_no_error = true; }, } } else if context_attributes.robustness != Robustness::NotRobust { return Err(ErrorKind::NotSupported( "WGL_ARB_create_context_robustness is not supported", ) .into()); } // Debug flag. if context_attributes.debug && !requested_no_error { flags |= wgl_extra::CONTEXT_DEBUG_BIT_ARB as c_int; } if flags != 0 { attrs.push(wgl_extra::CONTEXT_FLAGS_ARB as c_int); attrs.push(flags as c_int); } // Flush control. if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) { match context_attributes.release_behavior { // This is the default behavior in specification. // // XXX even though we check for extensions don't pass it because it could cause // issues. ReleaseBehavior::Flush => (), ReleaseBehavior::None => { attrs.push(wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); attrs.push(wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int); }, } } else if context_attributes.release_behavior != ReleaseBehavior::Flush { return Err(ErrorKind::NotSupported( "flush control behavior WGL_ARB_context_flush_control", ) .into()); } // Terminate list with zero. attrs.push(0); unsafe { let raw = extra.CreateContextAttribsARB(hdc as _, share_context, attrs.as_ptr()); if raw.is_null() { Err(IoError::last_os_error().into()) } else { Ok((WglContext(raw), supports_surfaceless)) } } } } /// A wrapper around the WGL context that is known to be not current to the /// calling thread. #[derive(Debug)] pub struct NotCurrentContext { inner: ContextInner, } impl Sealed for NotCurrentContext {} impl NotCurrentContext { fn new(inner: ContextInner) -> Self { Self { inner } } /// Make a [`Self::PossiblyCurrentContext`] indicating that the context /// could be current on the thread. /// /// Requires the WGL_ARB_create_context extension and OpenGL 3.0 or greater. pub fn make_current_surfaceless(self) -> Result { self.inner.make_current_surfaceless()?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } } impl NotCurrentGlContext for NotCurrentContext { type PossiblyCurrentContext = PossiblyCurrentContext; type Surface = Surface; fn treat_as_possibly_current(self) -> PossiblyCurrentContext { PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } } fn make_current( self, surface: &Self::Surface, ) -> Result { self.inner.make_current(surface)?; Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) } fn make_current_draw_read( self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result { Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) } } impl GlContext for NotCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl GetGlDisplay for NotCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl GetGlConfig for NotCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl AsRawContext for NotCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Wgl(*self.inner.raw) } } /// A wrapper around WGL context that could be current to the calling thread. #[derive(Debug)] pub struct PossiblyCurrentContext { inner: ContextInner, // The context could be current only on the one thread. _nosendsync: PhantomData, } impl PossiblyCurrentContext { /// Make this context current on the calling thread. /// /// Requires the WGL_ARB_create_context extension and OpenGL 3.0 or greater. pub fn make_current_surfaceless(&self) -> Result<()> { self.inner.make_current_surfaceless() } } impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; type Surface = Surface; fn make_not_current(self) -> Result { self.make_not_current_in_place()?; Ok(NotCurrentContext::new(self.inner)) } fn make_not_current_in_place(&self) -> Result<()> { unsafe { if self.is_current() { let hdc = wgl::GetCurrentDC(); if wgl::MakeCurrent(hdc, std::ptr::null()) == 0 { return Err(IoError::last_os_error().into()); } } } Ok(()) } fn is_current(&self) -> bool { unsafe { wgl::GetCurrentContext() == *self.inner.raw } } fn make_current(&self, surface: &Self::Surface) -> Result<()> { self.inner.make_current(surface) } fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()> { Err(self.inner.make_current_draw_read(surface_draw, surface_read).into()) } } impl Sealed for PossiblyCurrentContext {} impl GetGlDisplay for PossiblyCurrentContext { type Target = Display; fn display(&self) -> Self::Target { self.inner.display.clone() } } impl GetGlConfig for PossiblyCurrentContext { type Target = Config; fn config(&self) -> Self::Target { self.inner.config.clone() } } impl GlContext for PossiblyCurrentContext { fn context_api(&self) -> ContextApi { self.inner.context_api() } fn priority(&self) -> Priority { Priority::Medium } } impl AsRawContext for PossiblyCurrentContext { fn raw_context(&self) -> RawContext { RawContext::Wgl(*self.inner.raw) } } struct ContextInner { display: Display, config: Config, raw: WglContext, is_gles: bool, supports_surfaceless: bool, } impl fmt::Debug for ContextInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") .field("config", &self.config.inner.pixel_format_index) .field("raw", &self.raw) .finish() } } #[derive(Debug)] struct WglContext(HGLRC); impl Deref for WglContext { type Target = HGLRC; fn deref(&self) -> &Self::Target { &self.0 } } unsafe impl Send for WglContext {} impl ContextInner { fn make_current_surfaceless(&self) -> Result<()> { if !self.supports_surfaceless { return Err( ErrorKind::NotSupported("the surfaceless context Api isn't supported").into() ); } unsafe { if wgl::MakeCurrent(std::ptr::null(), self.raw.cast()) == 0 { Err(IoError::last_os_error().into()) } else { Ok(()) } } } fn make_current_draw_read( &self, _surface_draw: &Surface, _surface_read: &Surface, ) -> ErrorKind { ErrorKind::NotSupported("make_current_draw_read is not supported by WGL") } fn make_current(&self, surface: &Surface) -> Result<()> { unsafe { if wgl::MakeCurrent(surface.raw.hdc() as _, self.raw.cast()) == 0 { Err(IoError::last_os_error().into()) } else { Ok(()) } } } fn context_api(&self) -> ContextApi { if self.is_gles { ContextApi::Gles(None) } else { ContextApi::OpenGl(None) } } } impl Drop for ContextInner { fn drop(&mut self) { unsafe { wgl::DeleteContext(*self.raw); } } } glutin-0.32.2/src/api/wgl/display.rs000064400000000000000000000201451046102023000153600ustar 00000000000000//! WGL display initialization and extension loading. use std::borrow::Cow; use std::collections::HashSet; use std::ffi::{self, CStr, OsStr}; use std::os::windows::ffi::OsStrExt; use std::sync::Arc; use std::{env, fmt}; use glutin_wgl_sys::wgl; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use windows_sys::Win32::Foundation::HMODULE; use windows_sys::Win32::Graphics::Gdi::HDC; use windows_sys::Win32::System::LibraryLoader as dll_loader; use crate::config::ConfigTemplate; use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; use super::config::Config; use super::context::NotCurrentContext; use super::surface::Surface; use super::WglExtra; /// The name of the OpenGL DLL to use. pub(crate) static GLUTIN_WGL_OPENGL_DLL_ENV: &str = "GLUTIN_WGL_OPENGL_DLL"; /// A WGL display. #[derive(Debug, Clone)] pub struct Display { pub(crate) inner: Arc, } impl Display { /// Create WGL display. /// /// The `native_window` is used to perform extension loading. If it's not /// passed the OpenGL will be limited to what it can do, though, basic /// operations could still be performed. /// /// You can alter OpenGL DLL name by setting `GLUTIN_WGL_OPENGL_DLL` /// environment variable. The default one is `opengl32.dll`. /// /// # Safety /// /// The `native_window` must point to the valid platform window and have /// valid `hinstance`. pub unsafe fn new( display: RawDisplayHandle, native_window: Option, ) -> Result { if !matches!(display, RawDisplayHandle::Windows(..)) { return Err(ErrorKind::NotSupported("provided native display is not supported").into()); } let dll_name: Cow<'_, str> = env::var(GLUTIN_WGL_OPENGL_DLL_ENV) .map(Into::into) .unwrap_or_else(|_| Cow::Borrowed("opengl32.dll")); let name = OsStr::new(dll_name.as_ref()).encode_wide().chain(Some(0)).collect::>(); let lib_opengl32 = unsafe { dll_loader::LoadLibraryW(name.as_ptr()) }; if lib_opengl32 == 0 { return Err(ErrorKind::NotFound.into()); } // In case native window was provided init extra functions. let (wgl_extra, client_extensions) = if let Some(RawWindowHandle::Win32(window)) = native_window { unsafe { let (wgl_extra, client_extensions) = super::load_extra_functions( window.hinstance.unwrap().get() as _, window.hwnd.get() as _, )?; (Some(wgl_extra), client_extensions) } } else { (None, HashSet::new()) }; let features = Self::extract_display_features(&client_extensions); let inner = Arc::new(DisplayInner { lib_opengl32, wgl_extra, features, client_extensions }); Ok(Display { inner }) } fn extract_display_features(extensions: &HashSet<&'static str>) -> DisplayFeatures { let mut features = DisplayFeatures::empty(); features.set( DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, extensions.contains("WGL_ARB_multisample"), ); features.set( DisplayFeatures::FLOAT_PIXEL_FORMAT, extensions.contains("WGL_ARB_pixel_format_float"), ); features.set( DisplayFeatures::SRGB_FRAMEBUFFERS, extensions.contains("WGL_ARB_framebuffer_sRGB") || extensions.contains("WGL_EXT_framebuffer_sRGB") || extensions.contains("WGL_EXT_colorspace"), ); features.set( DisplayFeatures::CREATE_ES_CONTEXT, extensions.contains("WGL_EXT_create_context_es2_profile") || extensions.contains("WGL_EXT_create_context_es_profile"), ); features.set(DisplayFeatures::SWAP_CONTROL, extensions.contains("WGL_EXT_swap_control")); features.set( DisplayFeatures::CONTEXT_ROBUSTNESS, extensions.contains("WGL_ARB_create_context_robustness"), ); features.set( DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, extensions.contains("WGL_ARB_context_flush_control"), ); features.set( DisplayFeatures::CONTEXT_NO_ERROR, extensions.contains("WGL_ARB_create_context_no_error"), ); features } } impl GlDisplay for Display { type Config = Config; type NotCurrentContext = NotCurrentContext; type PbufferSurface = Surface; type PixmapSurface = Surface; type WindowSurface = Surface; unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( &self, config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { unsafe { let addr = addr.as_ptr(); let fn_ptr = wgl::GetProcAddress(addr); if !fn_ptr.is_null() { fn_ptr.cast() } else { dll_loader::GetProcAddress(self.inner.lib_opengl32, addr.cast()) .map_or(std::ptr::null(), |fn_ptr| fn_ptr as *const _) } } } fn version_string(&self) -> String { String::from("WGL") } fn supported_features(&self) -> DisplayFeatures { self.inner.features } } impl GetDisplayExtensions for Display { fn extensions(&self) -> &HashSet<&'static str> { &self.inner.client_extensions } } impl AsRawDisplay for Display { fn raw_display(&self) -> RawDisplay { RawDisplay::Wgl } } impl Sealed for Display {} pub(crate) struct DisplayInner { /// Client WGL extensions. pub(crate) lib_opengl32: HMODULE, /// Extra functions used by the impl. pub(crate) wgl_extra: Option<&'static WglExtra>, pub(crate) features: DisplayFeatures, pub(crate) client_extensions: HashSet<&'static str>, } impl fmt::Debug for DisplayInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Display") .field("features", &self.features) .field("extensions", &self.client_extensions) .finish() } } pub(crate) fn load_extensions(hdc: HDC, wgl_extra: &WglExtra) -> HashSet<&'static str> { let extensions = unsafe { if wgl_extra.GetExtensionsStringARB.is_loaded() { CStr::from_ptr(wgl_extra.GetExtensionsStringARB(hdc as *const _)) } else if wgl_extra.GetExtensionsStringEXT.is_loaded() { CStr::from_ptr(wgl_extra.GetExtensionsStringEXT()) } else { return HashSet::new(); } }; if let Ok(extensions) = extensions.to_str() { extensions.split(' ').collect::>() } else { HashSet::new() } } glutin-0.32.2/src/api/wgl/mod.rs000064400000000000000000000104151046102023000144710ustar 00000000000000//! WGL Api. use std::collections::HashSet; use std::ffi::{CString, OsStr}; use std::io::Error as IoError; use std::mem; use std::ops::Deref; use std::os::windows::ffi::OsStrExt; use glutin_wgl_sys::{wgl, wgl_extra}; use once_cell::sync::OnceCell; use windows_sys::Win32::Foundation::{HMODULE, HWND}; use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; use windows_sys::Win32::UI::WindowsAndMessaging::{self as wm, WINDOWPLACEMENT, WNDCLASSEXW}; use crate::error::{Error, ErrorKind, Result}; pub mod config; pub mod context; pub mod display; pub mod surface; pub(crate) static WGL_EXTRA: OnceCell = OnceCell::new(); pub(crate) struct WglExtra(wgl_extra::Wgl); unsafe impl Send for WglExtra {} unsafe impl Sync for WglExtra {} impl WglExtra { fn new() -> Self { Self(wgl_extra::Wgl::load_with(|addr| unsafe { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); wgl::GetProcAddress(addr).cast() })) } } impl Deref for WglExtra { type Target = wgl_extra::Wgl; fn deref(&self) -> &Self::Target { &self.0 } } unsafe fn load_extra_functions( instance: HMODULE, win: HWND, ) -> Result<(&'static WglExtra, HashSet<&'static str>)> { let rect = unsafe { let mut placement: WINDOWPLACEMENT = std::mem::zeroed(); placement.length = mem::size_of::() as _; if wm::GetWindowPlacement(win, &mut placement) == 0 { return Err(IoError::last_os_error().into()); } placement.rcNormalPosition }; let mut class_name = [0u16; 128]; unsafe { if wm::GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { return Err(IoError::last_os_error().into()); } } let mut class = unsafe { let mut class: WNDCLASSEXW = std::mem::zeroed(); if wm::GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 { return Err(IoError::last_os_error().into()); } class }; let class_name = OsStr::new("WglDummy Window").encode_wide().chain(Some(0)).collect::>(); class.cbSize = mem::size_of::() as _; class.lpszClassName = class_name.as_ptr(); class.lpfnWndProc = Some(wm::DefWindowProcW); // This shouldn't fail if the registration of the real window class // worked. Multiple registrations of the window class trigger an // error which we want to ignore silently (e.g for multi-window // setups). unsafe { wm::RegisterClassExW(&class) }; // This dummy window should match the real one enough to get the same OpenGL // driver. let title = OsStr::new("dummy window").encode_wide().chain(Some(0)).collect::>(); let ex_style = wm::WS_EX_APPWINDOW; let style = wm::WS_POPUP | wm::WS_CLIPSIBLINGS | wm::WS_CLIPCHILDREN; let win = unsafe { wm::CreateWindowExW( ex_style, class_name.as_ptr(), title.as_ptr() as _, style, wm::CW_USEDEFAULT, wm::CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, 0, 0, instance, std::ptr::null_mut(), ) }; if win == 0 { return Err(IoError::last_os_error().into()); } let hdc = unsafe { gdi::GetDC(win) }; unsafe { let (pixel_format_index, descriptor) = config::choose_dummy_pixel_format(hdc)?; if gl::SetPixelFormat(hdc, pixel_format_index, &descriptor) == 0 { return Err(IoError::last_os_error().into()); } }; let context = unsafe { let context = gl::wglCreateContext(hdc); if gl::wglMakeCurrent(hdc, context) == 0 { return Err(IoError::last_os_error().into()); } context }; // Load WGL. let wgl_extra = WGL_EXTRA.get_or_init(WglExtra::new); let client_extensions = display::load_extensions(hdc, wgl_extra); unsafe { wm::DestroyWindow(win); gl::wglDeleteContext(context); } Ok((wgl_extra, client_extensions)) } impl From for Error { fn from(error: std::io::Error) -> Self { let raw = error.raw_os_error().map(|code| code as i64); Error::new(raw, Some(error.to_string()), ErrorKind::Misc) } } glutin-0.32.2/src/api/wgl/surface.rs000064400000000000000000000224551046102023000153510ustar 00000000000000//! A wrapper around `HWND` used for GL operations. use std::io::Error as IoError; use std::marker::PhantomData; use std::num::NonZeroU32; use std::os::raw::c_int; use std::{fmt, mem}; use glutin_wgl_sys::wgl::types::GLenum; use glutin_wgl_sys::wgl_extra::types::HPBUFFEREXT; use glutin_wgl_sys::wgl_extra::{self}; use raw_window_handle::RawWindowHandle; use windows_sys::Win32::Foundation::{HWND, RECT}; use windows_sys::Win32::Graphics::Gdi::HDC; use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; use windows_sys::Win32::UI::WindowsAndMessaging::GetClientRect; use crate::config::GetGlConfig; use crate::display::{DisplayFeatures, GetGlDisplay}; use crate::error::{ErrorKind, Result}; use crate::prelude::*; use crate::private::Sealed; use crate::surface::{ AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes, SurfaceTypeTrait, SwapInterval, WindowSurface, }; use super::config::Config; use super::context::PossiblyCurrentContext; use super::display::Display; impl Display { pub(crate) unsafe fn create_pixmap_surface( &self, _config: &Config, _surface_attributes: &SurfaceAttributes, ) -> Result> { Err(ErrorKind::NotSupported("pixmaps are not implemented with WGL").into()) } pub(crate) unsafe fn create_pbuffer_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let extra = self .inner .wgl_extra .filter(|_| self.inner.client_extensions.contains("WGL_ARB_pbuffer")) .ok_or(ErrorKind::NotSupported("pbuffer extensions are not supported"))?; let hdc = config.inner.hdc; let width = surface_attributes.width.unwrap().get() as c_int; let height = surface_attributes.height.unwrap().get() as c_int; let mut attrs = [0; 3]; if surface_attributes.largest_pbuffer { attrs[0] = wgl_extra::PBUFFER_LARGEST_ARB as c_int; attrs[1] = 1; } let hbuf = unsafe { extra.CreatePbufferARB( hdc as _, config.inner.pixel_format_index, width, height, attrs.as_ptr(), ) }; if hbuf.is_null() { return Err(IoError::last_os_error().into()); } let hdc = unsafe { extra.GetPbufferDCARB(hbuf) }; if hdc.is_null() { return Err(IoError::last_os_error().into()); } let surface = Surface { display: self.clone(), config: config.clone(), raw: WglSurface::PBuffer(hbuf, hdc as _), _ty: PhantomData, }; Ok(surface) } pub(crate) unsafe fn create_window_surface( &self, config: &Config, surface_attributes: &SurfaceAttributes, ) -> Result> { let hwnd = match surface_attributes.raw_window_handle.as_ref().unwrap() { handle @ RawWindowHandle::Win32(window_handle) => { let _ = unsafe { config.apply_on_native_window(handle) }; window_handle.hwnd.get() as HWND }, _ => { return Err( ErrorKind::NotSupported("provided native window is not supported").into() ) }, }; let hdc = unsafe { gdi::GetDC(hwnd) }; let surface = Surface { display: self.clone(), config: config.clone(), raw: WglSurface::Window(hwnd, hdc), _ty: PhantomData, }; Ok(surface) } } /// A Wrapper around `WglSurface`. pub struct Surface { display: Display, config: Config, pub(crate) raw: WglSurface, _ty: PhantomData, } // Impl only `Send` for Surface. unsafe impl Send for Surface {} impl Surface { fn raw_attribute(&self, attr: GLenum) -> Option { match self.raw { WglSurface::Window(..) => None, WglSurface::PBuffer(hbuf, _) => { let extra = self.display.inner.wgl_extra.unwrap(); let mut value = 0; if unsafe { extra.QueryPbufferARB(hbuf, attr as _, &mut value) } == false.into() { None } else { Some(value) } }, } } } impl Drop for Surface { fn drop(&mut self) { unsafe { match self.raw { WglSurface::Window(hwnd, hdc) => { gdi::ReleaseDC(hwnd, hdc); }, WglSurface::PBuffer(hbuf, hdc) => { let extra = self.display.inner.wgl_extra.unwrap(); extra.ReleasePbufferDCARB(hbuf, hdc as _); extra.DestroyPbufferARB(hbuf); }, } } } } impl GlSurface for Surface { type Context = PossiblyCurrentContext; type SurfaceType = T; fn buffer_age(&self) -> u32 { 0 } fn width(&self) -> Option { match self.raw { WglSurface::Window(hwnd, _) => { let mut rect: RECT = unsafe { mem::zeroed() }; if unsafe { GetClientRect(hwnd, &mut rect) } == false.into() { None } else { Some((rect.right - rect.left) as u32) } }, WglSurface::PBuffer(..) => { self.raw_attribute(wgl_extra::PBUFFER_WIDTH_ARB).map(|x| x as _) }, } } fn height(&self) -> Option { match self.raw { WglSurface::Window(hwnd, _) => { let mut rect: RECT = unsafe { mem::zeroed() }; if unsafe { GetClientRect(hwnd, &mut rect) } == false.into() { None } else { Some((rect.bottom - rect.top) as u32) } }, WglSurface::PBuffer(..) => { self.raw_attribute(wgl_extra::PBUFFER_HEIGHT_ARB).map(|x| x as _) }, } } fn is_single_buffered(&self) -> bool { self.config.is_single_buffered() } fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { unsafe { if gl::SwapBuffers(self.raw.hdc()) == 0 { Err(IoError::last_os_error().into()) } else { Ok(()) } } } fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { match self.raw { WglSurface::Window(..) => { let extra = self .display .inner .wgl_extra .filter(|_| self.display.inner.features.contains(DisplayFeatures::SWAP_CONTROL)) .ok_or(ErrorKind::NotSupported("swap control extensions are not supported"))?; let interval = match interval { SwapInterval::DontWait => 0, SwapInterval::Wait(n) => n.get(), }; if unsafe { extra.SwapIntervalEXT(interval as _) } == 0 { Err(IoError::last_os_error().into()) } else { Ok(()) } }, _ => Err(ErrorKind::NotSupported("swap control not supported for surface").into()), } } fn is_current(&self, context: &Self::Context) -> bool { context.is_current() } fn is_current_draw(&self, context: &Self::Context) -> bool { context.is_current() } fn is_current_read(&self, context: &Self::Context) -> bool { context.is_current() } fn resize(&self, _context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) { // This isn't supported with WGL. } } impl fmt::Debug for Surface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Surface") .field("config", &self.config.inner.pixel_format_index) .field("raw", &self.raw) .finish() } } impl AsRawSurface for Surface { fn raw_surface(&self) -> RawSurface { match self.raw { WglSurface::Window(hwnd, _) => RawSurface::Wgl(hwnd as _), WglSurface::PBuffer(hbuf, _) => RawSurface::Wgl(hbuf as _), } } } impl GetGlConfig for Surface { type Target = Config; fn config(&self) -> Self::Target { self.config.clone() } } impl GetGlDisplay for Surface { type Target = Display; fn display(&self) -> Self::Target { self.display.clone() } } impl Sealed for Surface {} /// A wrapper around WGL surfaces. #[derive(Debug)] pub(crate) enum WglSurface { /// Surface backed by a window surface. Window(HWND, HDC), /// Surface backed by a pixel buffer. PBuffer(HPBUFFEREXT, HDC), } impl WglSurface { pub(crate) fn hdc(&self) -> HDC { *match self { WglSurface::Window(_, hdc) => hdc, WglSurface::PBuffer(_, hdc) => hdc, } } } glutin-0.32.2/src/config.rs000064400000000000000000000357221046102023000136250ustar 00000000000000//! Api config picking and creating utils. #![allow(unreachable_patterns)] use std::num::NonZeroU32; use bitflags::bitflags; use raw_window_handle::RawWindowHandle; use crate::display::{Display, GetGlDisplay}; use crate::private::{gl_api_dispatch, Sealed}; #[cfg(x11_platform)] use crate::platform::x11::{X11GlConfigExt, X11VisualInfo}; #[cfg(cgl_backend)] use crate::api::cgl::config::Config as CglConfig; #[cfg(egl_backend)] use crate::api::egl::config::Config as EglConfig; #[cfg(glx_backend)] use crate::api::glx::config::Config as GlxConfig; #[cfg(wgl_backend)] use crate::api::wgl::config::Config as WglConfig; /// The trait to group all common config option. pub trait GlConfig: Sealed { /// The type of the underlying color buffer. /// /// `None` is returned when the format can not be identified. fn color_buffer_type(&self) -> Option; /// Whether the config uses floating pixels. fn float_pixels(&self) -> bool; /// The size of the alpha. fn alpha_size(&self) -> u8; /// The size of the depth buffer. fn depth_size(&self) -> u8; /// The size of the stencil buffer. fn stencil_size(&self) -> u8; /// The number of samples in multisample buffer. /// /// Zero would mean that there're no samples. fn num_samples(&self) -> u8; /// Whether the config supports creating srgb capable [`Surface`]. /// /// [`Surface`]: crate::surface::Surface fn srgb_capable(&self) -> bool; /// Whether the config supports creating transparent surfaces. /// /// This function will return `None` when the property couldn't be /// identified, in that case transparent window could still work. fn supports_transparency(&self) -> Option; /// Whether the config is hardware accelerated. /// /// The meaning of this may vary from system to system. On some it could /// mean that you're using a software backend renderer, it could mean /// that you're using not the fastest available GPU, like in laptops /// with hybrid graphics. fn hardware_accelerated(&self) -> bool; /// The type of the surfaces that can be created with this config. fn config_surface_types(&self) -> ConfigSurfaceTypes; /// The [`crate::config::Api`] supported by the configuration. fn api(&self) -> Api; } /// The trait to pub trait GetGlConfig: Sealed { /// The config type. type Target: GlConfig; /// Get the GL config used to create a particular GL object. fn config(&self) -> Self::Target; } /// Get the raw config. pub trait AsRawConfig { /// Obtain the [`RawConfig`] of the underlying Api. fn raw_config(&self) -> RawConfig; } /// Builder for the [`ConfigTemplate`]. #[derive(Debug, Default, Clone)] pub struct ConfigTemplateBuilder { template: ConfigTemplate, } impl ConfigTemplateBuilder { /// Create a new configuration template builder. #[inline] pub fn new() -> Self { Default::default() } /// Number of alpha bits in the color buffer. /// /// By default `8` is requested. #[inline] pub fn with_alpha_size(mut self, alpha_size: u8) -> Self { self.template.alpha_size = alpha_size; self } /// Whether the floating pixel formats should be used. /// /// By default `false` is requested. #[inline] pub fn with_float_pixels(mut self, float_pixels: bool) -> Self { self.template.float_pixels = float_pixels; self } /// Number of bits in the stencil buffer. /// /// By default `0` is requested. #[inline] pub fn with_stencil_size(mut self, stencil_size: u8) -> Self { self.template.stencil_size = stencil_size; self } /// Number of bits in the depth buffer. /// /// By default `0` is requested. #[inline] pub fn with_depth_size(mut self, depth_size: u8) -> Self { self.template.depth_size = depth_size; self } /// Whether multisampling configurations should be picked. The `num_samples` /// must be a power of two. /// /// By default multisampling is not specified. #[inline] pub fn with_multisampling(mut self, num_samples: u8) -> Self { debug_assert!(num_samples.is_power_of_two()); self.template.num_samples = Some(num_samples); self } /// The types of the surfaces that must be supported by the configuration. /// /// By default only the `WINDOW` bit is set. #[inline] pub fn with_surface_type(mut self, config_surface_types: ConfigSurfaceTypes) -> Self { self.template.config_surface_types = config_surface_types; self } /// The type of the color buffer. /// /// By default `RGB` buffer with all components sizes of `8` is requested. #[inline] pub fn with_buffer_type(mut self, color_buffer_type: ColorBufferType) -> Self { self.template.color_buffer_type = color_buffer_type; self } /// The set of apis that are supported by this configuration. /// /// The default [`Api`] depends on the used graphics platform interface. If /// you want to do config filtering based on the [`Api`] yourself, use /// [`Api::empty`]. /// /// # Api-specific /// /// - **EGL:** [`Api::GLES2`] bit is set by default to avoid matching /// [`Api::GLES1`] configs; /// - **GLX/WGL/CGL:** [`Api::OPENGL`] is always present in the result. #[inline] pub fn with_api(mut self, api: Api) -> Self { self.template.api = Some(api); self } /// Whether the stereo pairs should be present. /// /// By default it isn't specified. #[inline] pub fn with_stereoscopy(mut self, stereoscopy: Option) -> Self { self.template.stereoscopy = stereoscopy; self } /// Whether the single buffer should be used. /// /// By default `false` is requested. #[inline] pub fn with_single_buffering(mut self, single_buffering: bool) -> Self { self.template.single_buffering = single_buffering; self } /// Whether the configuration should support transparency. /// /// The default is `false`. /// /// # Api-specific /// /// EGL on X11 doesn't provide a way to create a transparent surface at the /// time of writing. Use GLX for that instead. #[inline] pub fn with_transparency(mut self, transparency: bool) -> Self { self.template.transparency = transparency; self } /// With the maximum sizes of pbuffer. #[inline] pub fn with_pbuffer_sizes(mut self, width: NonZeroU32, height: NonZeroU32) -> Self { self.template.max_pbuffer_width = Some(width.into()); self.template.max_pbuffer_height = Some(height.into()); self } /// Whether the configuration should prefer hardware accelerated formats or /// not. /// /// By default hardware acceleration or its absence is not requested. pub fn prefer_hardware_accelerated(mut self, hardware_accerelated: Option) -> Self { self.template.hardware_accelerated = hardware_accerelated; self } /// Request config that can render to a particular native window. /// /// # Platform-specific /// /// This will use native window when matching the config to get the best one /// suitable for rendering into that window. /// /// When using WGL it's the most reliable way to get a working /// configuration. With GLX it'll use the visual passed in /// `native_window` to match the config. pub fn compatible_with_native_window(mut self, native_window: RawWindowHandle) -> Self { self.template.native_window = Some(native_window); self } /// With supported swap intervals. /// /// By default the value isn't specified. //// /// # Api-specific /// /// Only supported with `EGL`. #[inline] pub fn with_swap_interval( mut self, min_swap_interval: Option, max_swap_interval: Option, ) -> Self { self.template.min_swap_interval = min_swap_interval; self.template.max_swap_interval = max_swap_interval; self } /// Build the template to match the configs against. #[must_use] pub fn build(self) -> ConfigTemplate { self.template } } /// The context configuration template that is used to find desired config. #[derive(Debug, Clone)] pub struct ConfigTemplate { /// The type of the backing buffer and ancillary buffers. pub(crate) color_buffer_type: ColorBufferType, /// Bits of alpha in the color buffer. pub(crate) alpha_size: u8, /// Bits of depth in the depth buffer. pub(crate) depth_size: u8, /// Bits of stencil in the stencil buffer. pub(crate) stencil_size: u8, /// The amount of samples in multisample buffer. pub(crate) num_samples: Option, /// The minimum swap interval supported by the configuration. pub(crate) min_swap_interval: Option, /// The maximum swap interval supported by the configuration. pub(crate) max_swap_interval: Option, /// The types of the surfaces supported by the configuration. pub(crate) config_surface_types: ConfigSurfaceTypes, /// The rendering Api's supported by the configuration. pub(crate) api: Option, /// The config should support transparency. pub(crate) transparency: bool, /// The config should prefer single buffering. pub(crate) single_buffering: bool, /// The config supports stereoscopy. pub(crate) stereoscopy: Option, /// The config uses floating pixels. pub(crate) float_pixels: bool, /// The maximum width of the pbuffer. pub(crate) max_pbuffer_width: Option, /// The config should prefer hardware accelerated formats. pub(crate) hardware_accelerated: Option, /// The maximum height of the pbuffer. pub(crate) max_pbuffer_height: Option, /// The native window config should support rendering into. pub(crate) native_window: Option, } impl Default for ConfigTemplate { fn default() -> Self { ConfigTemplate { color_buffer_type: ColorBufferType::Rgb { r_size: 8, g_size: 8, b_size: 8 }, alpha_size: 8, depth_size: 24, stencil_size: 8, num_samples: None, transparency: false, stereoscopy: None, min_swap_interval: None, max_swap_interval: None, single_buffering: false, float_pixels: false, config_surface_types: ConfigSurfaceTypes::WINDOW, max_pbuffer_width: None, max_pbuffer_height: None, native_window: None, hardware_accelerated: None, api: None, } } } bitflags! { /// The types of the surface supported by the config. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ConfigSurfaceTypes: u8 { /// Context must support windows. const WINDOW = 0b00000001; /// Context must support pixmaps. const PIXMAP = 0b00000010; /// Context must support pbuffers. const PBUFFER = 0b00000100; } } bitflags! { /// The Api supported by the config. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Api : u8 { /// Context supports OpenGL API. const OPENGL = 0b00000001; /// Context supports OpenGL ES 1 API. const GLES1 = 0b00000010; /// Context supports OpenGL ES 2 API. const GLES2 = 0b00000100; /// Context supports OpenGL ES 3 API. const GLES3 = 0b00001000; } } /// The buffer type baked by the config. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ColorBufferType { /// The backing buffer is using RGB format. Rgb { /// Size of the red component in bits. r_size: u8, /// Size of the green component in bits. g_size: u8, /// Size of the blue component in bits. b_size: u8, }, /// The backing buffer is using Luminance. Luminance(u8), } /// The GL configuration used to create [`Surface`] and [`Context`] in a cross /// platform way. /// /// The config could be accessed from any thread. /// /// ```no_run /// fn test_send() {} /// fn test_sync() {} /// test_send::(); /// test_sync::(); /// ``` /// /// [`Surface`]: crate::surface::Surface /// [`Context`]: crate::context::NotCurrentContext #[derive(Debug, Clone, PartialEq, Eq)] pub enum Config { /// The EGL config. #[cfg(egl_backend)] Egl(EglConfig), /// The GLX config. #[cfg(glx_backend)] Glx(GlxConfig), /// The WGL config. #[cfg(wgl_backend)] Wgl(WglConfig), /// The CGL config. #[cfg(cgl_backend)] Cgl(CglConfig), } impl GlConfig for Config { fn color_buffer_type(&self) -> Option { gl_api_dispatch!(self; Self(config) => config.color_buffer_type()) } fn float_pixels(&self) -> bool { gl_api_dispatch!(self; Self(config) => config.float_pixels()) } fn alpha_size(&self) -> u8 { gl_api_dispatch!(self; Self(config) => config.alpha_size()) } fn depth_size(&self) -> u8 { gl_api_dispatch!(self; Self(config) => config.depth_size()) } fn stencil_size(&self) -> u8 { gl_api_dispatch!(self; Self(config) => config.stencil_size()) } fn num_samples(&self) -> u8 { gl_api_dispatch!(self; Self(config) => config.num_samples()) } fn srgb_capable(&self) -> bool { gl_api_dispatch!(self; Self(config) => config.srgb_capable()) } fn config_surface_types(&self) -> ConfigSurfaceTypes { gl_api_dispatch!(self; Self(config) => config.config_surface_types()) } fn hardware_accelerated(&self) -> bool { gl_api_dispatch!(self; Self(config) => config.hardware_accelerated()) } fn supports_transparency(&self) -> Option { gl_api_dispatch!(self; Self(config) => config.supports_transparency()) } fn api(&self) -> Api { gl_api_dispatch!(self; Self(config) => config.api()) } } impl GetGlDisplay for Config { type Target = Display; fn display(&self) -> Self::Target { gl_api_dispatch!(self; Self(config) => config.display(); as Display) } } #[cfg(x11_platform)] impl X11GlConfigExt for Config { fn x11_visual(&self) -> Option { gl_api_dispatch!(self; Self(config) => config.x11_visual()) } } impl Sealed for Config {} /// Raw config. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RawConfig { /// Raw EGL config. #[cfg(egl_backend)] Egl(*const std::ffi::c_void), /// Raw GLX config. #[cfg(glx_backend)] Glx(*const std::ffi::c_void), /// WGL pixel format index. #[cfg(wgl_backend)] Wgl(i32), /// NSOpenGLPixelFormat. #[cfg(cgl_backend)] Cgl(*const std::ffi::c_void), } impl AsRawConfig for Config { fn raw_config(&self) -> RawConfig { gl_api_dispatch!(self; Self(config) => config.raw_config()) } } glutin-0.32.2/src/context.rs000064400000000000000000000544461046102023000140500ustar 00000000000000//! OpenGL context creation and initialization. #![allow(unreachable_patterns)] use std::ffi; use raw_window_handle::RawWindowHandle; use crate::config::{Config, GetGlConfig}; use crate::display::{Display, GetGlDisplay}; use crate::error::Result; use crate::private::{gl_api_dispatch, Sealed}; use crate::surface::{GlSurface, Surface, SurfaceTypeTrait}; #[cfg(cgl_backend)] use crate::api::cgl::context::{ NotCurrentContext as NotCurrentCglContext, PossiblyCurrentContext as PossiblyCurrentCglContext, }; #[cfg(egl_backend)] use crate::api::egl::context::{ NotCurrentContext as NotCurrentEglContext, PossiblyCurrentContext as PossiblyCurrentEglContext, }; #[cfg(glx_backend)] use crate::api::glx::context::{ NotCurrentContext as NotCurrentGlxContext, PossiblyCurrentContext as PossiblyCurrentGlxContext, }; #[cfg(wgl_backend)] use crate::api::wgl::context::{ NotCurrentContext as NotCurrentWglContext, PossiblyCurrentContext as PossiblyCurrentWglContext, }; /// A trait to group common context operations. pub trait GlContext: Sealed { /// Get the [`ContextApi`] used by the context. /// /// The returned value's [`Version`] will always be `None`. fn context_api(&self) -> ContextApi; /// Get the [`Priority`] used by the context. fn priority(&self) -> Priority; } /// A trait to group common not current operations. pub trait NotCurrentGlContext: Sealed { /// The type of possibly current context. type PossiblyCurrentContext: PossiblyCurrentGlContext; /// The surface supported by the context. type Surface: GlSurface; /// Treat the not current context as possibly current. The operation is safe /// because the possibly current context is more restricted and not /// guaranteed to be current. fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext; /// Make [`Self::Surface`] on the calling thread producing the /// [`Self::PossiblyCurrentContext`] indicating that the context could /// be current on the thread. /// /// # Platform specific /// /// - **macOS: this will block if your main thread is blocked**; /// - **Wayland:** this call may latch the underlying back buffer (will do /// with mesa drivers), meaning that all resize operations will apply /// after the next [`GlSurface::swap_buffers`]. fn make_current( self, surface: &Self::Surface, ) -> Result; /// The same as [`Self::make_current`], but provides a way to set read and /// draw surfaces. /// /// # Api-specific: /// /// - **WGL/CGL:** not supported. fn make_current_draw_read( self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result; } /// A trait to group common context operations. pub trait PossiblyCurrentGlContext: Sealed { /// The not current context type. type NotCurrentContext: NotCurrentGlContext; /// The surface supported by the context. type Surface: GlSurface; /// Returns `true` if this context is the current one in this thread. fn is_current(&self) -> bool; /// Make the context not current to the current thread and returns a /// [`Self::NotCurrentContext`] to indicate that the context is a not /// current to allow sending it to the different thread. /// /// # Platform specific /// /// - **macOS: this will block if your main thread is blocked.** fn make_not_current(self) -> Result; /// Make the context not current to the current thread. If you need to /// send the context to another thread, use [`Self::make_not_current`] /// instead. fn make_not_current_in_place(&self) -> Result<()>; /// Make [`Self::Surface`] current on the calling thread. /// /// # Platform specific /// /// - **macOS: this will block if your main thread is blocked.** fn make_current(&self, surface: &Self::Surface) -> Result<()>; /// The same as [`Self::make_current`] but provides a way to set read and /// draw surfaces explicitly. /// /// # Api-specific: /// /// - **CGL/WGL:** not supported. fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()>; } /// A trait that provides raw context. pub trait AsRawContext { /// Get the raw context handle. fn raw_context(&self) -> RawContext; } /// The builder to help customizing context #[derive(Default, Debug, Clone)] pub struct ContextAttributesBuilder { attributes: ContextAttributes, } impl ContextAttributesBuilder { /// Create new builder. pub fn new() -> Self { Default::default() } /// Sets the *debug* flag for the OpenGL context. /// /// Debug contexts are usually slower, but give better error reporting. /// This option is ignored when using [`Robustness::NoError`]. /// /// The default value for this flag is `false`. pub fn with_debug(mut self, debug: bool) -> Self { self.attributes.debug = debug; self } /// Share the display lists with the given context. /// /// To get sharing working it's recommended to use the same [`Config`] when /// creating contexts that are going to be shared. /// /// # Platform-specific /// /// - **Wayland:** both contexts must use the same Wayland connection. /// /// [`Config`]: crate::config::Config pub fn with_sharing(mut self, context: &impl AsRawContext) -> Self { self.attributes.shared_context = Some(context.raw_context()); self } /// Sets the robustness of the OpenGL context. See the docs of /// [`Robustness`]. /// /// The default is [`Robustness::NotRobust`], because this is what typically /// expected when you create an OpenGL context. However for safety you /// should consider [`Robustness::RobustLoseContextOnReset`]. pub fn with_robustness(mut self, robustness: Robustness) -> Self { self.attributes.robustness = robustness; self } /// The behavior when changing the current context. See the docs of /// [`ReleaseBehavior`]. /// /// The default is [`ReleaseBehavior::Flush`]. pub fn with_release_behavior(mut self, release_behavior: ReleaseBehavior) -> Self { self.attributes.release_behavior = release_behavior; self } /// Set the desired OpenGL context profile. See the docs of [`GlProfile`]. /// /// By default the profile is unspecified. /// /// # Api-specific /// /// - **macOS:** not supported, the latest is picked automatically. pub fn with_profile(mut self, profile: GlProfile) -> Self { self.attributes.profile = Some(profile); self } /// Set the desired OpenGL context api. See the docs of [`ContextApi`]. /// /// By default the supported api will be picked. pub fn with_context_api(mut self, api: ContextApi) -> Self { self.attributes.api = Some(api); self } /// Set the priority hint, which might not be honored if the API does not /// support it, if there are constraints on the number of high priority /// contexts available in the system, or system policy limits access to /// high priority contexts to appropriate system privilege level the /// context creation may fail. /// /// By default no priority is specified, which corresponds to /// [`Priority::Medium`]. /// /// # Api specific /// /// - **WGL/GLX:** not implemented. /// - **CGL:** not supported. pub fn with_priority(mut self, priority: Priority) -> Self { self.attributes.priority = Some(priority); self } /// Build the context attributes. /// /// The `raw_window_handle` isn't required and here for WGL compatibility. /// /// # Api-specific /// /// - **WGL:** you **must** pass a `raw_window_handle` if you plan to use /// this context with that window. pub fn build(mut self, raw_window_handle: Option) -> ContextAttributes { self.attributes.raw_window_handle = raw_window_handle; self.attributes } } /// The attributes that are used to create a graphics context. #[derive(Default, Debug, Clone)] pub struct ContextAttributes { pub(crate) release_behavior: ReleaseBehavior, pub(crate) debug: bool, pub(crate) robustness: Robustness, pub(crate) profile: Option, pub(crate) api: Option, pub(crate) priority: Option, pub(crate) shared_context: Option, pub(crate) raw_window_handle: Option, } /// Specifies the tolerance of the OpenGL context to faults. If you accept /// raw OpenGL commands and/or raw shader code from an untrusted source, you /// should definitely care about this. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum Robustness { /// Not everything is checked. Your application can crash if you do /// something wrong with your shaders. #[default] NotRobust, /// The driver doesn't check anything. This option is very dangerous. /// Please know what you're doing before using it. See the /// `GL_KHR_no_error` extension. /// /// Since this option is purely an optimization, no error will be returned /// if the backend doesn't support it. Instead it will automatically /// fall back to [`Robustness::NotRobust`]. NoError, /// Everything is checked to avoid any crash. The driver will attempt to /// avoid any problem, but if a problem occurs the behavior is /// implementation-defined. You are just guaranteed not to get a crash. RobustNoResetNotification, /// Everything is checked to avoid any crash. If a problem occurs, the /// context will enter a "context lost" state. It must then be /// recreated. RobustLoseContextOnReset, } /// Describes the requested OpenGL context profiles. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GlProfile { /// Include all the future-compatible functions and definitions. /// /// The requested OpenGL version with [`ContextApi`] should be at least 3.3. Core, /// Include all the immediate more functions and definitions. /// /// Use it only when it's really needed, otherwise use [`Self::Core`]. Compatibility, } /// The rendering Api context should support. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ContextApi { /// OpenGL Api version that should be used by the context. /// /// When using `None` as `Version` any OpenGL context will be picked, /// however when the [`GlProfile::Core`] is used at least 3.3 will be /// requested. OpenGl(Option), /// OpenGL Api version that should be used by the context. /// /// When using `None` as `Version` the latest **known** major version is /// picked. Versions that are higher than what was picked automatically /// could still be supported. Gles(Option), } #[cfg(any(egl_backend, glx_backend, wgl_backend))] impl ContextApi { pub(crate) fn version(&self) -> Option { match self { Self::OpenGl(version) => *version, Self::Gles(version) => *version, _ => None, } } } impl Default for ContextApi { fn default() -> Self { Self::OpenGl(None) } } /// The version used to index the Api. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Version { /// Major version of the Api. pub major: u8, /// Minor version of the Api. pub minor: u8, } impl Version { /// Create new version with the given `major` and `minor` values. pub const fn new(major: u8, minor: u8) -> Self { Self { major, minor } } } /// The behavior of the driver when you change the current context. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ReleaseBehavior { /// Doesn't do anything. Most notably doesn't flush. Not supported by all /// drivers. /// /// # Api-specific /// /// - **macOS:** not supported, [`Self::Flush`] is always used. None, /// Flushes the context that was previously current as if `glFlush` was /// called. This is the default behavior. #[default] Flush, } /// A context that is known to be not current on the current thread. /// /// This type is a safe wrapper around the context to indicate that it could be /// `Send` to the different thread, since the context must be not current before /// doing so. /// /// ```no_run /// fn test_send() {} /// test_send::(); /// ``` /// However it's not `Sync`. /// ```compile_fail /// fn test_sync() {} /// test_sync::(); /// ``` #[derive(Debug)] pub enum NotCurrentContext { /// The EGL context. #[cfg(egl_backend)] Egl(NotCurrentEglContext), /// The GLX context. #[cfg(glx_backend)] Glx(NotCurrentGlxContext), /// The WGL context. #[cfg(wgl_backend)] Wgl(NotCurrentWglContext), /// The CGL context. #[cfg(cgl_backend)] Cgl(NotCurrentCglContext), } impl NotCurrentGlContext for NotCurrentContext { type PossiblyCurrentContext = PossiblyCurrentContext; type Surface = Surface; fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext { gl_api_dispatch!(self; Self(context) => context.treat_as_possibly_current(); as PossiblyCurrentContext) } fn make_current( self, surface: &Self::Surface, ) -> Result { match (self, surface) { #[cfg(egl_backend)] (Self::Egl(context), Surface::Egl(surface)) => { Ok(PossiblyCurrentContext::Egl(context.make_current(surface)?)) }, #[cfg(glx_backend)] (Self::Glx(context), Surface::Glx(surface)) => { Ok(PossiblyCurrentContext::Glx(context.make_current(surface)?)) }, #[cfg(wgl_backend)] (Self::Wgl(context), Surface::Wgl(surface)) => { Ok(PossiblyCurrentContext::Wgl(context.make_current(surface)?)) }, #[cfg(cgl_backend)] (Self::Cgl(context), Surface::Cgl(surface)) => { Ok(PossiblyCurrentContext::Cgl(context.make_current(surface)?)) }, _ => unreachable!(), } } fn make_current_draw_read( self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result { match (self, surface_draw, surface_read) { #[cfg(egl_backend)] (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { Ok(PossiblyCurrentContext::Egl(context.make_current_draw_read(draw, read)?)) }, #[cfg(glx_backend)] (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { Ok(PossiblyCurrentContext::Glx(context.make_current_draw_read(draw, read)?)) }, #[cfg(wgl_backend)] (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { Ok(PossiblyCurrentContext::Wgl(context.make_current_draw_read(draw, read)?)) }, #[cfg(cgl_backend)] (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { Ok(PossiblyCurrentContext::Cgl(context.make_current_draw_read(draw, read)?)) }, _ => unreachable!(), } } } impl GlContext for NotCurrentContext { fn context_api(&self) -> ContextApi { gl_api_dispatch!(self; Self(context) => context.context_api()) } fn priority(&self) -> Priority { gl_api_dispatch!(self; Self(context) => context.priority()) } } impl GetGlConfig for NotCurrentContext { type Target = Config; fn config(&self) -> Self::Target { gl_api_dispatch!(self; Self(context) => context.config(); as Config) } } impl GetGlDisplay for NotCurrentContext { type Target = Display; fn display(&self) -> Self::Target { gl_api_dispatch!(self; Self(context) => context.display(); as Display) } } impl AsRawContext for NotCurrentContext { fn raw_context(&self) -> RawContext { gl_api_dispatch!(self; Self(context) => context.raw_context()) } } impl Sealed for NotCurrentContext {} /// A context that is possibly current on the current thread. /// /// The context that could be current on the current thread can neither be /// [`Send`] nor [`Sync`]. In case you need to use it on a different thread /// [make it not current]. /// ```compile_fail /// fn test_send() {} /// test_send::(); /// ``` /// /// ```compile_fail /// fn test_sync() {} /// test_sync::(); /// ``` /// /// [make it not current]: crate::context::PossiblyCurrentGlContext::make_not_current #[derive(Debug)] pub enum PossiblyCurrentContext { /// The EGL context. #[cfg(egl_backend)] Egl(PossiblyCurrentEglContext), /// The GLX context. #[cfg(glx_backend)] Glx(PossiblyCurrentGlxContext), /// The WGL context. #[cfg(wgl_backend)] Wgl(PossiblyCurrentWglContext), /// The CGL context. #[cfg(cgl_backend)] Cgl(PossiblyCurrentCglContext), } impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; type Surface = Surface; fn is_current(&self) -> bool { gl_api_dispatch!(self; Self(context) => context.is_current()) } fn make_not_current(self) -> Result { Ok( gl_api_dispatch!(self; Self(context) => context.make_not_current()?; as NotCurrentContext), ) } fn make_not_current_in_place(&self) -> Result<()> { Ok(gl_api_dispatch!(self; Self(context) => context.make_not_current_in_place()?)) } fn make_current(&self, surface: &Self::Surface) -> Result<()> { match (self, surface) { #[cfg(egl_backend)] (Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface), #[cfg(glx_backend)] (Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface), #[cfg(wgl_backend)] (Self::Wgl(context), Surface::Wgl(surface)) => context.make_current(surface), #[cfg(cgl_backend)] (Self::Cgl(context), Surface::Cgl(surface)) => context.make_current(surface), _ => unreachable!(), } } fn make_current_draw_read( &self, surface_draw: &Self::Surface, surface_read: &Self::Surface, ) -> Result<()> { match (self, surface_draw, surface_read) { #[cfg(egl_backend)] (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { context.make_current_draw_read(draw, read) }, #[cfg(glx_backend)] (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { context.make_current_draw_read(draw, read) }, #[cfg(wgl_backend)] (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { context.make_current_draw_read(draw, read) }, #[cfg(cgl_backend)] (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { context.make_current_draw_read(draw, read) }, _ => unreachable!(), } } } impl GlContext for PossiblyCurrentContext { fn context_api(&self) -> ContextApi { gl_api_dispatch!(self; Self(context) => context.context_api()) } fn priority(&self) -> Priority { gl_api_dispatch!(self; Self(context) => context.priority()) } } impl GetGlConfig for PossiblyCurrentContext { type Target = Config; fn config(&self) -> Self::Target { gl_api_dispatch!(self; Self(context) => context.config(); as Config) } } impl GetGlDisplay for PossiblyCurrentContext { type Target = Display; fn display(&self) -> Self::Target { gl_api_dispatch!(self; Self(context) => context.display(); as Display) } } impl AsRawContext for PossiblyCurrentContext { fn raw_context(&self) -> RawContext { gl_api_dispatch!(self; Self(context) => context.raw_context()) } } impl Sealed for PossiblyCurrentContext {} /// Raw context. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RawContext { /// Raw EGL context. #[cfg(egl_backend)] Egl(*const ffi::c_void), /// Raw GLX context. #[cfg(glx_backend)] Glx(*const ffi::c_void), /// HGLRC pointer. #[cfg(wgl_backend)] Wgl(*const ffi::c_void), /// Pointer to NSOpenGLContext. #[cfg(cgl_backend)] Cgl(*const ffi::c_void), } /// Priority hint #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum Priority { /// Lowest priority, contexts using this priority give way for most other /// contexts. Low, /// Default priority. #[default] Medium, /// High priority is usually required for VR applications. High, /// Realtime priority contexts are executed immediately and preempt any /// current context running. /// /// When such context is not supported, [`Priority::High`] will be requested /// instead. Realtime, } /// Pick `GlProfile` and `Version` based on the provided params. #[cfg(any(egl_backend, glx_backend, wgl_backend))] pub(crate) fn pick_profile( profile: Option, version: Option, ) -> (GlProfile, Version) { match (profile, version) { (Some(GlProfile::Core), Some(version)) => (GlProfile::Core, version), (Some(GlProfile::Compatibility), Some(version)) => (GlProfile::Compatibility, version), (None, Some(version)) if version >= Version::new(3, 3) => (GlProfile::Core, version), (None, Some(version)) => (GlProfile::Compatibility, version), (Some(GlProfile::Core), None) => (GlProfile::Core, Version::new(3, 3)), (Some(GlProfile::Compatibility), None) => (GlProfile::Compatibility, Version::new(2, 1)), (None, None) => (GlProfile::Core, Version::new(3, 3)), } } glutin-0.32.2/src/display.rs000064400000000000000000000512731046102023000140240ustar 00000000000000//! The OpenGL platform display selection and creation. #![allow(unreachable_patterns)] use std::collections::HashSet; use std::ffi::{self, CStr}; use std::fmt; use bitflags::bitflags; use raw_window_handle::RawDisplayHandle; use crate::config::{Config, ConfigTemplate, GlConfig}; use crate::context::{ContextAttributes, NotCurrentContext, NotCurrentGlContext}; use crate::error::Result; use crate::private::{gl_api_dispatch, Sealed}; use crate::surface::{ GlSurface, PbufferSurface, PixmapSurface, Surface, SurfaceAttributes, WindowSurface, }; #[cfg(cgl_backend)] use crate::api::cgl::display::Display as CglDisplay; #[cfg(egl_backend)] use crate::api::egl::display::Display as EglDisplay; #[cfg(glx_backend)] use crate::api::glx::display::Display as GlxDisplay; #[cfg(glx_backend)] use crate::api::glx::XlibErrorHookRegistrar; #[cfg(wgl_backend)] use crate::api::wgl::display::Display as WglDisplay; /// A trait to group common display operations. pub trait GlDisplay: Sealed { /// A window surface created by the display. type WindowSurface: GlSurface; /// A pixmap surface created by the display. type PixmapSurface: GlSurface; /// A pbuffer surface created by the display. type PbufferSurface: GlSurface; /// A config that is used by the display. type Config: GlConfig; /// A context that is being used by the display. type NotCurrentContext: NotCurrentGlContext; /// Find configurations matching the given `template`. /// /// # Safety /// /// Some platforms use [`RawWindowHandle`] to pick configs, so it /// must point to a valid object if it was passed on /// [`crate::config::ConfigTemplate`]. /// /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>>; /// Create the graphics platform context. /// /// # Safety /// /// Some platforms use [`RawWindowHandle`] for context creation, so it must /// point to a valid object. /// /// # Platform-specific /// /// - **Wayland:** this call may latch the underlying back buffer of the /// currently active context (will do with mesa drivers), meaning that all /// resize operations will apply to it after the next /// [`GlSurface::swap_buffers`]. To workaround this behavior the current /// context should be made [`not current`]. /// /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle /// [`not current`]: crate::context::PossiblyCurrentGlContext::make_not_current unsafe fn create_context( &self, config: &Self::Config, context_attributes: &ContextAttributes, ) -> Result; /// Create the surface that can be used to render into native window. /// /// # Safety /// /// The [`RawWindowHandle`] must point to a valid object. /// /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result; /// Create the surface that can be used to render into pbuffer. /// /// # Safety /// /// The function is safe in general, but marked as not for compatibility /// reasons. unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result; /// Create the surface that can be used to render into pixmap. /// /// # Safety /// /// The [`NativePixmap`] must represent a valid native pixmap. /// /// [`NativePixmap`]: crate::surface::NativePixmap unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result; /// Return the address of an OpenGL function. /// /// # Api-specific /// /// - **WGL:** to load all the functions you must have a current context on /// the calling thread, otherwise only a limited set of functions will be /// loaded. fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void; /// Helper to obtain the information about the underlying display. /// /// This function is intended to be used for logging purposes to help with /// troubleshooting issues. fn version_string(&self) -> String; /// Get the features supported by the display. /// /// These features could be used to check that something is supported /// beforehand instead of doing fallback. fn supported_features(&self) -> DisplayFeatures; } /// Get the [`Display`]. pub trait GetGlDisplay: Sealed { /// The display used by the object. type Target: GlDisplay; /// Obtain the GL display used to create a particular GL object. fn display(&self) -> Self::Target; } /// Obtain the underlying api extensions. pub trait GetDisplayExtensions: Sealed { /// Supported extensions by the display. /// /// # Api-specific /// /// - **WGL:** to have extensions loaded, `raw_window_handle` must be used /// when creating the display. fn extensions(&self) -> &HashSet<&'static str>; } /// Get the raw handle to the [`Display`]. pub trait AsRawDisplay { /// A raw handle to the underlying Api display. fn raw_display(&self) -> RawDisplay; } /// The graphics display to handle underlying graphics platform in a /// cross-platform way. /// /// The display can be accessed from any thread. /// /// ```no_run /// fn test_send() {} /// fn test_sync() {} /// test_send::(); /// test_sync::(); /// ``` #[derive(Debug, Clone)] pub enum Display { /// The EGL display. #[cfg(egl_backend)] Egl(EglDisplay), /// The GLX display. #[cfg(glx_backend)] Glx(GlxDisplay), /// The WGL display. #[cfg(wgl_backend)] Wgl(WglDisplay), /// The CGL display. #[cfg(cgl_backend)] Cgl(CglDisplay), } impl Display { /// Create a graphics platform display from the given raw display handle. /// /// The display mixing isn't supported, so if you created EGL display you /// can't use it with the GLX display objects. Interaction between those /// will result in a runtime panic. /// /// # Safety /// /// The `display` must point to the valid platform display and be valid for /// the entire lifetime of all Objects created with that display. /// /// The `preference` must contain pointers to the valid values if GLX or WGL /// specific options were used. pub unsafe fn new(display: RawDisplayHandle, preference: DisplayApiPreference) -> Result { match preference { #[cfg(egl_backend)] DisplayApiPreference::Egl => unsafe { Ok(Self::Egl(EglDisplay::new(display)?)) }, #[cfg(glx_backend)] DisplayApiPreference::Glx(registrar) => unsafe { Ok(Self::Glx(GlxDisplay::new(display, registrar)?)) }, #[cfg(all(egl_backend, glx_backend))] DisplayApiPreference::GlxThenEgl(registrar) => unsafe { if let Ok(display) = GlxDisplay::new(display, registrar) { Ok(Self::Glx(display)) } else { Ok(Self::Egl(EglDisplay::new(display)?)) } }, #[cfg(all(egl_backend, glx_backend))] DisplayApiPreference::EglThenGlx(registrar) => unsafe { if let Ok(display) = EglDisplay::new(display) { Ok(Self::Egl(display)) } else { Ok(Self::Glx(GlxDisplay::new(display, registrar)?)) } }, #[cfg(wgl_backend)] DisplayApiPreference::Wgl(window_handle) => unsafe { Ok(Self::Wgl(WglDisplay::new(display, window_handle)?)) }, #[cfg(all(egl_backend, wgl_backend))] DisplayApiPreference::EglThenWgl(window_handle) => unsafe { if let Ok(display) = EglDisplay::new(display) { Ok(Self::Egl(display)) } else { Ok(Self::Wgl(WglDisplay::new(display, window_handle)?)) } }, #[cfg(all(egl_backend, wgl_backend))] DisplayApiPreference::WglThenEgl(window_handle) => unsafe { if let Ok(display) = WglDisplay::new(display, window_handle) { Ok(Self::Wgl(display)) } else { Ok(Self::Egl(EglDisplay::new(display)?)) } }, #[cfg(cgl_backend)] DisplayApiPreference::Cgl => unsafe { Ok(Self::Cgl(CglDisplay::new(display)?)) }, } } } impl GlDisplay for Display { type Config = Config; type NotCurrentContext = NotCurrentContext; type PbufferSurface = Surface; type PixmapSurface = Surface; type WindowSurface = Surface; unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { match self { #[cfg(egl_backend)] Self::Egl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.map(Config::Egl))) }, #[cfg(glx_backend)] Self::Glx(display) => unsafe { Ok(Box::new(display.find_configs(template)?.map(Config::Glx))) }, #[cfg(wgl_backend)] Self::Wgl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.map(Config::Wgl))) }, #[cfg(cgl_backend)] Self::Cgl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.map(Config::Cgl))) }, } } unsafe fn create_context( &self, config: &Self::Config, context_attributes: &ContextAttributes, ) -> Result { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { Ok(NotCurrentContext::Egl(display.create_context(config, context_attributes)?)) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { Ok(NotCurrentContext::Glx(display.create_context(config, context_attributes)?)) }, #[cfg(wgl_backend)] (Self::Wgl(display), Config::Wgl(config)) => unsafe { Ok(NotCurrentContext::Wgl(display.create_context(config, context_attributes)?)) }, #[cfg(cgl_backend)] (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(NotCurrentContext::Cgl(display.create_context(config, context_attributes)?)) }, _ => unreachable!(), } } unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?)) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { Ok(Surface::Glx(display.create_window_surface(config, surface_attributes)?)) }, #[cfg(wgl_backend)] (Self::Wgl(display), Config::Wgl(config)) => unsafe { Ok(Surface::Wgl(display.create_window_surface(config, surface_attributes)?)) }, #[cfg(cgl_backend)] (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_window_surface(config, surface_attributes)?)) }, _ => unreachable!(), } } unsafe fn create_pbuffer_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { Ok(Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)?)) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { Ok(Surface::Glx(display.create_pbuffer_surface(config, surface_attributes)?)) }, #[cfg(wgl_backend)] (Self::Wgl(display), Config::Wgl(config)) => unsafe { Ok(Surface::Wgl(display.create_pbuffer_surface(config, surface_attributes)?)) }, #[cfg(cgl_backend)] (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_pbuffer_surface(config, surface_attributes)?)) }, _ => unreachable!(), } } unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?)) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { Ok(Surface::Glx(display.create_pixmap_surface(config, surface_attributes)?)) }, #[cfg(wgl_backend)] (Self::Wgl(display), Config::Wgl(config)) => unsafe { Ok(Surface::Wgl(display.create_pixmap_surface(config, surface_attributes)?)) }, #[cfg(cgl_backend)] (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_pixmap_surface(config, surface_attributes)?)) }, _ => unreachable!(), } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { gl_api_dispatch!(self; Self(display) => display.get_proc_address(addr)) } fn version_string(&self) -> String { gl_api_dispatch!(self; Self(display) => display.version_string()) } fn supported_features(&self) -> DisplayFeatures { gl_api_dispatch!(self; Self(display) => display.supported_features()) } } impl AsRawDisplay for Display { fn raw_display(&self) -> RawDisplay { gl_api_dispatch!(self; Self(display) => display.raw_display()) } } impl Sealed for Display {} /// Preference of the display that should be used. pub enum DisplayApiPreference { /// Use only EGL. /// /// The EGL is a cross platform recent OpenGL platform. That being said /// it's usually lacking on Windows and not present at all on macOS /// natively. /// /// Be also aware that some features may not be present with it, like window /// transparency on X11 with mesa. /// /// But despite this issues it should be preferred on at least Linux over /// GLX, given that GLX is phasing away. /// /// # Platform-specific /// /// **Windows:** ANGLE can be used if `libEGL.dll` and `libGLESv2.dll` are /// in the library search path. #[cfg(egl_backend)] Egl, /// Use only GLX. /// /// The native GLX platform, it's not very optimal since it's usually tied /// to Xlib. It's know to work fine, but be aware that you must register /// glutin with your X11 error handling callback, since it's a /// per-process global state. /// /// The hook to register glutin error handler in the X11 error handling /// function. #[cfg(glx_backend)] Glx(XlibErrorHookRegistrar), /// Use only WGL. /// /// The most spread platform on Windows and what should be used on it by /// default. EGL usually not present there so you'd have to account for that /// and create the window beforehand. /// /// When raw window handle isn't provided the display will lack extensions /// support and most features will be lacking. #[cfg(wgl_backend)] Wgl(Option), /// Use only CGL. /// /// The only option on macOS for now. #[cfg(cgl_backend)] Cgl, /// Prefer EGL and fallback to GLX. /// /// See [`Egl`] and [`Glx`] to decide what you want. /// /// [`Egl`]: Self::Egl /// [`Glx`]: Self::Glx #[cfg(all(egl_backend, glx_backend))] EglThenGlx(XlibErrorHookRegistrar), /// Prefer GLX and fallback to EGL. /// /// See [`Egl`] and [`Glx`] to decide what you want. /// /// [`Egl`]: Self::Egl /// [`Glx`]: Self::Glx #[cfg(all(egl_backend, glx_backend))] GlxThenEgl(XlibErrorHookRegistrar), /// Prefer EGL and fallback to WGL. /// /// See [`Egl`] and [`Wgl`] to decide what you want. /// /// [`Egl`]: Self::Egl /// [`Wgl`]: Self::Wgl #[cfg(all(egl_backend, wgl_backend))] EglThenWgl(Option), /// Prefer WGL and fallback to EGL. /// /// See [`Egl`] and [`Wgl`] to decide what you want. /// /// [`Egl`]: Self::Egl /// [`Wgl`]: Self::Wgl #[cfg(all(egl_backend, wgl_backend))] WglThenEgl(Option), } impl fmt::Debug for DisplayApiPreference { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let api = match self { #[cfg(egl_backend)] DisplayApiPreference::Egl => "Egl", #[cfg(glx_backend)] DisplayApiPreference::Glx(_) => "Glx", #[cfg(all(egl_backend, glx_backend))] DisplayApiPreference::GlxThenEgl(_) => "GlxThenEgl", #[cfg(all(egl_backend, glx_backend))] DisplayApiPreference::EglThenGlx(_) => "EglThenGlx", #[cfg(wgl_backend)] DisplayApiPreference::Wgl(_) => "Wgl", #[cfg(all(egl_backend, wgl_backend))] DisplayApiPreference::EglThenWgl(_) => "EglThenWgl", #[cfg(all(egl_backend, wgl_backend))] DisplayApiPreference::WglThenEgl(_) => "WglThenEgl", #[cfg(cgl_backend)] DisplayApiPreference::Cgl => "Cgl", }; f.write_fmt(format_args!("DisplayApiPreference::{api}")) } } bitflags! { /// The features and extensions supported by the [`Display`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DisplayFeatures: u32 { /// The display supports creating [`robust`] context. /// /// [`robust`]: crate::context::Robustness const CONTEXT_ROBUSTNESS = 0b0000_0001; /// The display supports creating [`no error`] context. /// /// [`no error`]: crate::context::Robustness::NoError const CONTEXT_NO_ERROR = 0b0000_0010; /// The display supports [`floating`] pixel formats. /// /// [`floating`]: crate::config::ConfigTemplateBuilder::with_float_pixels const FLOAT_PIXEL_FORMAT = 0b0000_0100; /// The display supports changing the [`swap interval`] on surfaces. /// /// [`swap interval`]: crate::surface::GlSurface::set_swap_interval const SWAP_CONTROL = 0b0000_1000; /// The display supports creating context with explicit [`release behavior`]. /// /// [`release behavior`]: crate::context::ReleaseBehavior const CONTEXT_RELEASE_BEHAVIOR = 0b0001_0000; /// The display supports creating OpenGL ES [`context`]. /// /// [`context`]: crate::context::ContextApi::Gles const CREATE_ES_CONTEXT = 0b0010_0000; /// The display supports pixel formats with [`multisampling`]. /// /// [`multisampling`]: crate::config::ConfigTemplateBuilder::with_multisampling const MULTISAMPLING_PIXEL_FORMATS = 0b0100_0000; /// The display supports creating surfaces backed by [`SRGB`] framebuffers. /// /// [`SRGB`]: crate::surface::SurfaceAttributesBuilder::with_srgb const SRGB_FRAMEBUFFERS = 0b1000_0000; } } /// Raw GL platform display. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RawDisplay { /// Raw EGL display. #[cfg(egl_backend)] Egl(*const std::ffi::c_void), /// Raw GLX display. #[cfg(glx_backend)] Glx(*const std::ffi::c_void), /// Raw display is WGL. #[cfg(wgl_backend)] Wgl, /// Raw display is CGL. #[cfg(cgl_backend)] Cgl, } glutin-0.32.2/src/error.rs000064400000000000000000000116461046102023000135100ustar 00000000000000//! Glutin error handling. use std::fmt; /// A specialized [`Result`] type for graphics operations. pub type Result = std::result::Result; /// The error type for all the graphics platform operations. #[derive(Debug, Clone)] pub struct Error { /// The raw code of the underlying error. raw_code: Option, /// The raw message from the os in case it could be obtained. raw_os_message: Option, /// The simplified error kind to handle matching. kind: ErrorKind, } impl Error { #[allow(dead_code)] pub(crate) fn new( raw_code: Option, raw_os_message: Option, kind: ErrorKind, ) -> Self { Self { raw_code, raw_os_message, kind } } /// Helper to check that error is [`ErrorKind::NotSupported`]. #[inline] pub fn not_supported(&self) -> bool { matches!(&self.kind, ErrorKind::NotSupported(_)) } /// The underlying error kind. #[inline] pub fn error_kind(&self) -> ErrorKind { self.kind } /// The underlying raw code in case it's present. #[inline] pub fn raw_code(&self) -> Option { self.raw_code } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(raw_code) = self.raw_code { write!(f, "[{raw_code:x}] ")?; } let msg = if let Some(raw_os_message) = self.raw_os_message.as_ref() { raw_os_message } else { self.kind.as_str() }; write!(f, "{msg}") } } impl std::error::Error for Error {} /// Build an error with just a kind. impl From for Error { fn from(kind: ErrorKind) -> Self { Error { raw_code: None, raw_os_message: None, kind } } } /// A list specifying general categoires of native platform graphics interface /// errors. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum ErrorKind { /// The requested display wasn't found or some required symbol in it was /// missing. NotFound, /// Failed to perform resource initialization. InitializationFailed, /// Can't access a requested resource. /// /// For example when trying to make a context current while it's current on /// another thread. BadAccess, /// An operation could not be completed, because it failed to allocate /// enough memory. OutOfMemory, /// An recognized attribute value was passed. BadAttribute, /// The context is no longer valid. BadContext, /// The context is in bad state. BadContextState, /// Invalid config was passed. BadConfig, /// The current surface of the calling thread is no longer valid. BadCurrentSurface, /// The display is no longer valid. BadDisplay, /// The surface is invalid. BadSurface, /// The pbuffer is invalid. BadPbuffer, /// The pixmap is invalid. BadPixmap, /// Arguments are inconsistent. For example when shared contexts are not /// compatible. BadMatch, /// One or more argument values are invalid. BadParameter, /// Bad native pixmap was provided. BadNativePixmap, /// Bad native window was provided. BadNativeWindow, /// The context was lost. ContextLost, /// The operation is not supported by the platform. NotSupported(&'static str), /// The misc error that can't be classified occurred. Misc, } impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { use ErrorKind::*; match *self { NotFound => "not found", InitializationFailed => "initialization failed", BadAccess => "access to the resource failed", OutOfMemory => "out of memory", BadAttribute => "an unrecognized attribute or attribute value was passed", BadContext => "argument does not name a valid context", BadContextState => "the context is in a bad state", BadConfig => "argument does not name a valid config", BadCurrentSurface => "the current surface of the calling thread is no longer valid", BadDisplay => "argument does not name a valid display", BadSurface => "argument does not name a valid surface", BadPbuffer => "argument does not name a valid pbuffer", BadPixmap => "argument does not name a valid pixmap", BadMatch => "arguments are inconsistent", BadParameter => "one or more argument values are invalid", BadNativePixmap => "argument does not refer to a valid native pixmap", BadNativeWindow => "argument does not refer to a valid native window", ContextLost => "context loss", NotSupported(reason) => reason, Misc => "misc platform error", } } } impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } glutin-0.32.2/src/lib.rs000064400000000000000000000057571046102023000131330ustar 00000000000000//! The purpose of this library is to provide an OpenGL [`context`] for as many //! platforms as possible, abstracting away the underlying differences without //! losing access to platform specific extensions. //! //! However Glutin doesn't force users into using the cross platform //! abstractions. When only a particular [`api`] is desired, it can //! be used directly. //! //! The initialization starts by loading and connecting to the platform's //! graphics Api when creating a [`display`]. This object is used to create all //! the OpenGL objects, such as [`config`], [`context`], and [`surface`]. //! //! ## Environment variables //! //! `GLUTIN_WGL_OPENGL_DLL` - change the name of the OpenGL DLL to load. #![deny(rust_2018_idioms)] #![deny(rustdoc::broken_intra_doc_links)] #![deny(unsafe_op_in_unsafe_fn)] #![deny(improper_ctypes, improper_ctypes_definitions)] #![deny(clippy::all)] #![deny(missing_debug_implementations)] #![deny(missing_docs)] #![cfg_attr(clippy, deny(warnings))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] compile_error!("Please select at least one api backend"); pub mod api; pub mod config; pub mod context; pub mod display; pub mod error; pub mod platform; pub mod prelude; pub mod surface; #[cfg(any(egl_backend, glx_backend))] mod lib_loading; pub(crate) mod private { /// Prevent traits from being implemented downstream, since those are used /// purely for documentation organization and simplify platform api /// implementation maintenance. pub trait Sealed {} /// `gl_api_dispatch!(match expr; Enum(foo) => foo.something())` /// expands to the equivalent of /// ```ignore /// match self { /// Enum::Egl(foo) => foo.something(), /// Enum::Glx(foo) => foo.something(), /// Enum::Wgl(foo) => foo.something(), /// Enum::Cgl(foo) => foo.something(), /// } /// ``` /// The result can be converted to another enum by adding `; as AnotherEnum` macro_rules! gl_api_dispatch { ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => { match $what { #[cfg(egl_backend)] $enum::Egl($($c1)*) => $enum2::Egl($x), #[cfg(glx_backend)] $enum::Glx($($c1)*) => $enum2::Glx($x), #[cfg(wgl_backend)] $enum::Wgl($($c1)*) => $enum2::Wgl($x), #[cfg(cgl_backend)] $enum::Cgl($($c1)*) => $enum2::Cgl($x), } }; ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr) => { match $what { #[cfg(egl_backend)] $enum::Egl($($c1)*) => $x, #[cfg(glx_backend)] $enum::Glx($($c1)*) => $x, #[cfg(wgl_backend)] $enum::Wgl($($c1)*) => $x, #[cfg(cgl_backend)] $enum::Cgl($($c1)*) => $x, } }; } pub(crate) use gl_api_dispatch; } glutin-0.32.2/src/lib_loading.rs000064400000000000000000000024601046102023000146140ustar 00000000000000//! Library loading routines. use std::ops::{Deref, DerefMut}; use std::sync::Arc; use libloading::Library; #[cfg(windows)] use libloading::os::windows::{Library as WinLibrary, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS}; pub trait SymLoading { /// # Safety /// The library must be unsured to live long enough. unsafe fn load_with(lib: &Library) -> Self; } #[derive(Clone)] #[allow(missing_debug_implementations)] pub struct SymWrapper { sym: T, _lib: Arc, } impl SymWrapper { pub unsafe fn new(lib_paths: &[&str]) -> Result { unsafe { for path in lib_paths { #[cfg(windows)] let lib = WinLibrary::load_with_flags(path, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) .map(From::from); #[cfg(not(windows))] let lib = Library::new(path); if let Ok(lib) = lib { return Ok(SymWrapper { sym: T::load_with(&lib), _lib: Arc::new(lib) }); } } } Err(()) } } impl Deref for SymWrapper { type Target = T; fn deref(&self) -> &Self::Target { &self.sym } } impl DerefMut for SymWrapper { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.sym } } glutin-0.32.2/src/platform/mod.rs000064400000000000000000000001061046102023000147470ustar 00000000000000//! Platform-specific API helpers. #[cfg(x11_platform)] pub mod x11; glutin-0.32.2/src/platform/x11.rs000064400000000000000000000055061046102023000146120ustar 00000000000000//! Utilities to access X11 specific config properties. use std::mem; use once_cell::sync::Lazy; use x11_dl::xlib::{Display, XVisualInfo, Xlib}; #[cfg(egl_backend)] use x11_dl::xlib::{VisualIDMask, XID}; use x11_dl::xrender::Xrender; /// The XLIB handle. pub(crate) static XLIB: Lazy> = Lazy::new(|| Xlib::open().ok()); /// The XRENDER handle. static XRENDER: Lazy> = Lazy::new(|| Xrender::open().ok()); /// The GlConfig extension trait to get X11 specific properties from a config. pub trait X11GlConfigExt { /// The `X11VisualInfo` that must be used to initialize the Xlib window. fn x11_visual(&self) -> Option; } /// The X11 visual info. /// /// This must be used when building X11 window, so it'll be compatible with the /// underlying Api. #[derive(Debug)] pub struct X11VisualInfo { raw: *const XVisualInfo, transparency: bool, } impl X11VisualInfo { #[cfg(egl_backend)] pub(crate) unsafe fn from_xid(display: *mut Display, xid: XID) -> Option { let xlib = XLIB.as_ref().unwrap(); if xid == 0 { return None; } let raw = unsafe { let mut raw: XVisualInfo = std::mem::zeroed(); raw.visualid = xid; let mut num_visuals = 0; (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals) }; if raw.is_null() { return None; } let transparency = Self::has_non_zero_alpha(display, raw); Some(Self { raw, transparency }) } #[cfg(glx_backend)] pub(crate) unsafe fn from_raw(display: *mut Display, raw: *const XVisualInfo) -> Self { let transparency = Self::has_non_zero_alpha(display, raw); Self { raw, transparency } } /// Returns `true` if the visual has non-zero alpha mask. pub fn supports_transparency(&self) -> bool { self.transparency } /// Get XID of for this visual. pub fn visual_id(&self) -> std::ffi::c_ulong { unsafe { (*self.raw).visualid } } /// Convert the visual to the raw pointer. /// /// You must clear it with `XFree` after the use. pub fn into_raw(self) -> *const std::ffi::c_void { let raw = self.raw as *const _; mem::forget(self); raw } pub(crate) fn has_non_zero_alpha(display: *mut Display, raw: *const XVisualInfo) -> bool { let xrender = XRENDER.as_ref().unwrap(); unsafe { let visual_format = (xrender.XRenderFindVisualFormat)(display, (*raw).visual); (!visual_format.is_null()) .then(|| (*visual_format).direct.alphaMask != 0) .unwrap_or(false) } } } impl Drop for X11VisualInfo { fn drop(&mut self) { unsafe { (XLIB.as_ref().unwrap().XFree)(self.raw as *mut _); } } } glutin-0.32.2/src/prelude.rs000064400000000000000000000007321046102023000140110ustar 00000000000000//! The glutin prelude. //! //! The purpose of this module is to make accessing common imports more //! convenient. The prelude also imports traits shared by the implementations of //! graphics apis. //! //! ```no_run //! # #![allow(unused_imports)] //! use glutin::prelude::*; //! ``` pub use crate::config::GlConfig; pub use crate::context::{GlContext, NotCurrentGlContext, PossiblyCurrentGlContext}; pub use crate::display::GlDisplay; pub use crate::surface::GlSurface; glutin-0.32.2/src/surface.rs000064400000000000000000000432001046102023000137760ustar 00000000000000//! A cross platform OpenGL surface representation. #![allow(unreachable_patterns)] use std::marker::PhantomData; use std::num::NonZeroU32; use raw_window_handle::RawWindowHandle; use crate::context::{PossiblyCurrentContext, PossiblyCurrentGlContext}; use crate::display::{Display, GetGlDisplay}; use crate::error::Result; use crate::private::{gl_api_dispatch, Sealed}; #[cfg(cgl_backend)] use crate::api::cgl::surface::Surface as CglSurface; #[cfg(egl_backend)] use crate::api::egl::surface::Surface as EglSurface; #[cfg(glx_backend)] use crate::api::glx::surface::Surface as GlxSurface; #[cfg(wgl_backend)] use crate::api::wgl::surface::Surface as WglSurface; /// A trait to group common operations on the surface. pub trait GlSurface: Sealed { /// The type of the surface. type SurfaceType: SurfaceTypeTrait; /// The context to access surface data. type Context: PossiblyCurrentGlContext; /// The age of the back buffer of that surface. The `0` indicates that the /// buffer is either a new one or we failed to get the information about /// its age. In both cases you must redraw the entire buffer. /// /// # Platform-specific /// /// - **Wayland:** this call will latch the underlying back buffer, meaning /// that all resize operations will apply after the next /// [`GlSurface::swap_buffers`]. fn buffer_age(&self) -> u32; /// The **physical** width of the underlying surface. fn width(&self) -> Option; /// The **physical** height of the underlying surface. /// /// # Platform specific /// /// - **macOS: this will block if your main thread is blocked.** fn height(&self) -> Option; /// Check whether the surface is single buffered. /// /// # Platform specific /// /// - **macOS: this will block if your main thread is blocked.** fn is_single_buffered(&self) -> bool; /// Swaps the underlying back buffers when the surface is not single /// buffered. fn swap_buffers(&self, context: &Self::Context) -> Result<()>; /// Check whether the surface is current on to the current thread. fn is_current(&self, context: &Self::Context) -> bool; /// Check whether the surface is the current draw surface to the current /// thread. fn is_current_draw(&self, context: &Self::Context) -> bool; /// Check whether the surface is the current read surface to the current /// thread. fn is_current_read(&self, context: &Self::Context) -> bool; /// Set swap interval for the surface. /// /// See [`crate::surface::SwapInterval`] for details. fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()>; /// Resize the surface to a new size. /// /// This call is for compatibility reasons, on most platforms it's a no-op. /// It's recommended to call this function before doing any rendering and /// performing [`PossiblyCurrentGlContext::make_current`], and /// [`GlSurface::buffer_age`]. /// /// # Platform specific /// /// - **Wayland:** resizes the surface; /// - **macOS: this will block if your main thread is blocked;** /// - **Other:** no op. fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32) where Self::SurfaceType: ResizeableSurface; } /// The marker trait to indicate the type of the surface. pub trait SurfaceTypeTrait: Sealed { /// Get the type of the surface. fn surface_type() -> SurfaceType; } /// Marker indicating that the surface could be resized. pub trait ResizeableSurface: Sealed {} /// Trait for accessing the raw GL surface. pub trait AsRawSurface { /// Get the raw handle to the surface. fn raw_surface(&self) -> RawSurface; } /// Builder to get the required set of attributes initialized before hand. #[derive(Default, Debug, Clone)] pub struct SurfaceAttributesBuilder { attributes: SurfaceAttributes, } impl SurfaceAttributesBuilder { /// Get new surface attributes. pub fn new() -> Self { Default::default() } /// Specify whether the surface should support srgb or not. Passing `None` /// means you don't care. /// /// # Api-specific. /// /// This only controls EGL surfaces, other platforms use the context for /// that. pub fn with_srgb(mut self, srgb: Option) -> Self { self.attributes.srgb = srgb; self } } impl SurfaceAttributesBuilder { /// Specify whether the single buffer should be used instead of double /// buffering. This doesn't guarantee that the resulted buffer will have /// only single buffer, to know that the single buffer is actually used /// query the created surface with [`Surface::is_single_buffered`]. /// /// The surface is requested as double buffered by default. /// /// # Api-specific. /// /// This is EGL specific, other platforms use the context for that. pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { self.attributes.single_buffer = single_buffer; self } /// Build the surface attributes suitable to create a window surface. pub fn build( mut self, raw_window_handle: RawWindowHandle, width: NonZeroU32, height: NonZeroU32, ) -> SurfaceAttributes { self.attributes.raw_window_handle = Some(raw_window_handle); self.attributes.width = Some(width); self.attributes.height = Some(height); self.attributes } } impl SurfaceAttributesBuilder { /// Request the largest pbuffer. pub fn with_largest_pbuffer(mut self, largest_pbuffer: bool) -> Self { self.attributes.largest_pbuffer = largest_pbuffer; self } /// The same as in /// [`SurfaceAttributesBuilder::::with_single_buffer`]. pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { self.attributes.single_buffer = single_buffer; self } /// Build the surface attributes suitable to create a pbuffer surface. pub fn build( mut self, width: NonZeroU32, height: NonZeroU32, ) -> SurfaceAttributes { self.attributes.width = Some(width); self.attributes.height = Some(height); self.attributes } } impl SurfaceAttributesBuilder { /// Build the surface attributes suitable to create a pixmap surface. pub fn build(mut self, native_pixmap: NativePixmap) -> SurfaceAttributes { self.attributes.native_pixmap = Some(native_pixmap); self.attributes } } /// Attributes which are used for creating a particular surface. #[derive(Default, Debug, Clone)] pub struct SurfaceAttributes { pub(crate) srgb: Option, pub(crate) single_buffer: bool, pub(crate) width: Option, pub(crate) height: Option, pub(crate) largest_pbuffer: bool, pub(crate) raw_window_handle: Option, pub(crate) native_pixmap: Option, _ty: PhantomData, } /// Marker that used to type-gate methods for window. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct WindowSurface; impl SurfaceTypeTrait for WindowSurface { fn surface_type() -> SurfaceType { SurfaceType::Window } } impl ResizeableSurface for WindowSurface {} impl Sealed for WindowSurface {} /// Marker that used to type-gate methods for pbuffer. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct PbufferSurface; impl SurfaceTypeTrait for PbufferSurface { fn surface_type() -> SurfaceType { SurfaceType::Pbuffer } } impl Sealed for PbufferSurface {} /// Marker that used to type-gate methods for pixmap. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct PixmapSurface; impl SurfaceTypeTrait for PixmapSurface { fn surface_type() -> SurfaceType { SurfaceType::Pixmap } } impl Sealed for PixmapSurface {} /// The underlying type of the surface. #[derive(Debug, Clone, Copy)] pub enum SurfaceType { /// The window surface. Window, /// Pixmap surface. Pixmap, /// Pbuffer surface. Pbuffer, } /// The GL surface that is used for rendering. /// /// Similar to the context, the GL surface is [`Send`] but not [`Sync`]. This /// means it could be sent to a different thread as long as it is not current on /// another thread. /// /// ```no_run /// fn test_send() {} /// test_send::>(); /// ``` /// ```compile_fail /// fn test_sync() {} /// test_sync::>(); /// ``` #[derive(Debug)] pub enum Surface { /// The EGL surface. #[cfg(egl_backend)] Egl(EglSurface), /// The GLX surface. #[cfg(glx_backend)] Glx(GlxSurface), /// The WGL surface. #[cfg(wgl_backend)] Wgl(WglSurface), /// The CGL surface. #[cfg(cgl_backend)] Cgl(CglSurface), } impl GlSurface for Surface { type Context = PossiblyCurrentContext; type SurfaceType = T; fn buffer_age(&self) -> u32 { gl_api_dispatch!(self; Self(surface) => surface.buffer_age()) } fn width(&self) -> Option { gl_api_dispatch!(self; Self(surface) => surface.width()) } fn height(&self) -> Option { gl_api_dispatch!(self; Self(surface) => surface.height()) } fn is_single_buffered(&self) -> bool { gl_api_dispatch!(self; Self(surface) => surface.is_single_buffered()) } fn swap_buffers(&self, context: &Self::Context) -> Result<()> { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.swap_buffers(context) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.swap_buffers(context) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.swap_buffers(context) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.swap_buffers(context) }, _ => unreachable!(), } } fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.set_swap_interval(context, interval) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.set_swap_interval(context, interval) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.set_swap_interval(context, interval) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.set_swap_interval(context, interval) }, _ => unreachable!(), } } fn is_current(&self, context: &Self::Context) -> bool { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.is_current(context) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.is_current(context) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.is_current(context) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.is_current(context) }, _ => unreachable!(), } } fn is_current_draw(&self, context: &Self::Context) -> bool { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.is_current_draw(context) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.is_current_draw(context) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.is_current_draw(context) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.is_current_draw(context) }, _ => unreachable!(), } } fn is_current_read(&self, context: &Self::Context) -> bool { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.is_current_read(context) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.is_current_read(context) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.is_current_read(context) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.is_current_read(context) }, _ => unreachable!(), } } fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32) where Self::SurfaceType: ResizeableSurface, { match (self, context) { #[cfg(egl_backend)] (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { surface.resize(context, width, height) }, #[cfg(glx_backend)] (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { surface.resize(context, width, height) }, #[cfg(cgl_backend)] (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { surface.resize(context, width, height) }, #[cfg(wgl_backend)] (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { surface.resize(context, width, height) }, _ => unreachable!(), } } } impl GetGlDisplay for Surface { type Target = Display; fn display(&self) -> Self::Target { gl_api_dispatch!(self; Self(surface) => surface.display(); as Display) } } impl AsRawSurface for Surface { fn raw_surface(&self) -> RawSurface { gl_api_dispatch!(self; Self(surface) => surface.raw_surface()) } } impl Sealed for Surface {} /// A swap interval. /// /// The default swap interval for your [`Surface`] is platform-dependent. For /// example, on EGL it is `1` by default, but on GLX it is `0` by default. /// /// Please note that your application's desired swap interval may be overridden /// by external, driver-specific configuration, which means that you can't know /// in advance whether [`crate::surface::GlSurface::swap_buffers`] will block /// or not. /// /// # Platform specific /// /// - **Wayland:** when the window is hidden and [`SwapInterval::Wait`] is used /// [`GlSurface::swap_buffers`] and any functions based on it may block until /// the window is visible again. Using this variant is not recommended on /// Wayland and instead the throttling should be performed by [`frame /// callbacks`]. /// /// [`frame callbacks`]: https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface-request-frame #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SwapInterval { /// When this variant is used calling /// [`crate::surface::GlSurface::swap_buffers()`] will not block. DontWait, /// The swap is synchronized to the `n`'th video frame. This is typically /// set to `1` to enable vsync and prevent screen tearing. Wait(NonZeroU32), } /// A platform native pixmap. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum NativePixmap { /// XID of X11 pixmap. XlibPixmap(std::os::raw::c_ulong), /// XID of X11 pixmap from xcb. XcbPixmap(u32), /// HBITMAP handle for windows bitmap. WindowsPixmap(isize), } /// Handle to the raw OpenGL surface. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RawSurface { /// A pointer to EGLSurface. #[cfg(egl_backend)] Egl(*const std::ffi::c_void), /// GLXDrawable. #[cfg(glx_backend)] Glx(u64), /// Either a `HWND` or `HPBUFFEREXT` depending on [`SurfaceType`]. #[cfg(wgl_backend)] Wgl(*const std::ffi::c_void), /// Pointer to `NSView`. #[cfg(cgl_backend)] Cgl(*const std::ffi::c_void), } /// The rect that is being used in various surface operations. /// /// The origin is in the bottom left of the surface. #[repr(C)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] pub struct Rect { /// `X` of the origin. pub x: i32, /// `Y` of the origin. pub y: i32, /// Rect width. pub width: i32, /// Rect height. pub height: i32, } impl Rect { /// Helper to simplify rectangle creation. pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self { Self { x, y, width, height } } }