From 2bc3b701cd6e523dc03f7857014df99352dd5d4f Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 17 Jun 2022 20:56:40 +0300 Subject: [PATCH 1/8] Decouple glutin from winit Thit commit removes direct dependency on the winit and using raw_window_handle crate instead. This is a library rewrite, since winit was a heart of the glutin. Now everything is centered around `Display`, `Surface`, `Config`, and `Context` using `raw-window-handle` for those initialization. --- .github/PULL_REQUEST_TEMPLATE.md | 3 - .github/workflows/ci.yml | 86 +- CHANGELOG.md | 7 + Cargo.toml | 1 - ISSUES.md | 43 - README.md | 29 +- glutin/Cargo.toml | 79 +- glutin/build.rs | 24 + glutin/src/api/cgl/config.rs | 230 +++ glutin/src/api/cgl/context.rs | 298 ++++ glutin/src/api/cgl/display.rs | 88 ++ glutin/src/api/cgl/mod.rs | 62 + glutin/src/api/cgl/surface.rs | 158 ++ glutin/src/api/dlloader.rs | 66 - glutin/src/api/egl/config.rs | 377 +++++ glutin/src/api/egl/context.rs | 343 +++++ glutin/src/api/egl/display.rs | 349 +++++ glutin/src/api/egl/make_current_guard.rs | 98 -- glutin/src/api/egl/mod.rs | 1307 +---------------- glutin/src/api/egl/surface.rs | 512 +++++++ glutin/src/api/glx/config.rs | 346 +++++ glutin/src/api/glx/context.rs | 420 ++++++ glutin/src/api/glx/display.rs | 210 +++ glutin/src/api/glx/make_current_guard.rs | 78 - glutin/src/api/glx/mod.rs | 850 ++--------- glutin/src/api/glx/surface.rs | 297 ++++ glutin/src/api/ios/mod.rs | 450 ------ glutin/src/api/mod.rs | 10 +- glutin/src/api/osmesa/mod.rs | 224 --- glutin/src/api/wgl/config.rs | 501 +++++++ glutin/src/api/wgl/context.rs | 404 +++++ glutin/src/api/wgl/display.rs | 159 ++ glutin/src/api/wgl/make_current_guard.rs | 54 - glutin/src/api/wgl/mod.rs | 953 ++---------- glutin/src/api/wgl/surface.rs | 184 +++ glutin/src/config.rs | 484 ++++++ glutin/src/context.rs | 700 +++++++-- glutin/src/display.rs | 502 +++++++ glutin/src/error.rs | 177 +++ glutin/src/lib.rs | 721 +-------- glutin/src/lib_loading.rs | 55 + glutin/src/platform/android.rs | 23 - glutin/src/platform/ios.rs | 21 - glutin/src/platform/macos.rs | 22 - glutin/src/platform/mod.rs | 55 +- glutin/src/platform/unix.rs | 32 - glutin/src/platform/windows.rs | 25 - glutin/src/platform/x11.rs | 95 ++ glutin/src/platform_impl/android/mod.rs | 164 --- glutin/src/platform_impl/ios/mod.rs | 4 - glutin/src/platform_impl/macos/helpers.rs | 134 -- glutin/src/platform_impl/macos/mod.rs | 358 ----- glutin/src/platform_impl/mod.rs | 23 - glutin/src/platform_impl/unix/mod.rs | 461 ------ glutin/src/platform_impl/unix/wayland.rs | 195 --- glutin/src/platform_impl/unix/x11.rs | 670 --------- glutin/src/platform_impl/unix/x11/utils.rs | 70 - glutin/src/platform_impl/windows/mod.rs | 328 ----- glutin/src/prelude.rs | 17 + glutin/src/surface.rs | 501 +++++++ glutin/src/windowed.rs | 323 ---- glutin_egl_sys/Cargo.toml | 9 +- glutin_egl_sys/build.rs | 39 +- glutin_egl_sys/src/lib.rs | 21 +- glutin_examples/Cargo.toml | 17 +- glutin_examples/build.rs | 31 +- glutin_examples/examples/buffer_age.rs | 40 - glutin_examples/examples/damage.rs | 92 -- glutin_examples/examples/fullscreen.rs | 117 -- glutin_examples/examples/headless.rs | 151 -- glutin_examples/examples/multiwindow.rs | 65 - glutin_examples/examples/raw_context.rs | 129 -- glutin_examples/examples/sharing.rs | 142 -- glutin_examples/examples/support/mod.rs | 418 +----- glutin_examples/examples/transparent.rs | 41 - glutin_examples/examples/window.rs | 100 +- glutin_examples/ios-example/.gitignore | 1 - .../GlutinExample.xcodeproj/project.pbxproj | 343 ----- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../ios-example/GlutinExample/Info.plist | 41 - .../ios-example/GlutinExample/main.m | 16 - glutin_examples/ios-example/Makefile | 22 - glutin_examples/ios-example/README.md | 10 - glutin_examples/ios-example/rust/.gitignore | 1 - glutin_examples/ios-example/rust/Cargo.toml | 18 - glutin_examples/ios-example/rust/Makefile | 14 - glutin_examples/ios-example/rust/build.rs | 16 - .../ios-example/rust/glutin_ios_example.h | 3 - .../ios-example/rust/rust_logo.png | Bin 13976 -> 0 bytes glutin_examples/ios-example/rust/src/lib.rs | 59 - glutin_gles2_sys/src/lib.rs | 13 +- glutin_glx_sys/build.rs | 36 +- glutin_glx_sys/src/lib.rs | 28 +- glutin_wgl_sys/build.rs | 37 +- glutin_wgl_sys/src/lib.rs | 6 +- rustfmt.toml | 16 +- 97 files changed, 8099 insertions(+), 9468 deletions(-) delete mode 100644 ISSUES.md create mode 100644 glutin/build.rs create mode 100644 glutin/src/api/cgl/config.rs create mode 100644 glutin/src/api/cgl/context.rs create mode 100644 glutin/src/api/cgl/display.rs create mode 100644 glutin/src/api/cgl/mod.rs create mode 100644 glutin/src/api/cgl/surface.rs delete mode 100644 glutin/src/api/dlloader.rs create mode 100644 glutin/src/api/egl/config.rs create mode 100644 glutin/src/api/egl/context.rs create mode 100644 glutin/src/api/egl/display.rs delete mode 100644 glutin/src/api/egl/make_current_guard.rs create mode 100644 glutin/src/api/egl/surface.rs create mode 100644 glutin/src/api/glx/config.rs create mode 100644 glutin/src/api/glx/context.rs create mode 100644 glutin/src/api/glx/display.rs delete mode 100644 glutin/src/api/glx/make_current_guard.rs create mode 100644 glutin/src/api/glx/surface.rs delete mode 100644 glutin/src/api/ios/mod.rs delete mode 100644 glutin/src/api/osmesa/mod.rs create mode 100644 glutin/src/api/wgl/config.rs create mode 100644 glutin/src/api/wgl/context.rs create mode 100644 glutin/src/api/wgl/display.rs delete mode 100644 glutin/src/api/wgl/make_current_guard.rs create mode 100644 glutin/src/api/wgl/surface.rs create mode 100644 glutin/src/config.rs create mode 100644 glutin/src/display.rs create mode 100644 glutin/src/error.rs create mode 100644 glutin/src/lib_loading.rs delete mode 100644 glutin/src/platform/android.rs delete mode 100644 glutin/src/platform/ios.rs delete mode 100644 glutin/src/platform/macos.rs delete mode 100644 glutin/src/platform/unix.rs delete mode 100644 glutin/src/platform/windows.rs create mode 100644 glutin/src/platform/x11.rs delete mode 100644 glutin/src/platform_impl/android/mod.rs delete mode 100644 glutin/src/platform_impl/ios/mod.rs delete mode 100644 glutin/src/platform_impl/macos/helpers.rs delete mode 100644 glutin/src/platform_impl/macos/mod.rs delete mode 100644 glutin/src/platform_impl/mod.rs delete mode 100644 glutin/src/platform_impl/unix/mod.rs delete mode 100644 glutin/src/platform_impl/unix/wayland.rs delete mode 100644 glutin/src/platform_impl/unix/x11.rs delete mode 100644 glutin/src/platform_impl/unix/x11/utils.rs delete mode 100644 glutin/src/platform_impl/windows/mod.rs create mode 100644 glutin/src/prelude.rs create mode 100644 glutin/src/surface.rs delete mode 100644 glutin/src/windowed.rs delete mode 100644 glutin_examples/examples/buffer_age.rs delete mode 100644 glutin_examples/examples/damage.rs delete mode 100644 glutin_examples/examples/fullscreen.rs delete mode 100644 glutin_examples/examples/headless.rs delete mode 100644 glutin_examples/examples/multiwindow.rs delete mode 100644 glutin_examples/examples/raw_context.rs delete mode 100644 glutin_examples/examples/sharing.rs delete mode 100644 glutin_examples/examples/transparent.rs delete mode 100644 glutin_examples/ios-example/.gitignore delete mode 100644 glutin_examples/ios-example/GlutinExample.xcodeproj/project.pbxproj delete mode 100644 glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 glutin_examples/ios-example/GlutinExample/Info.plist delete mode 100644 glutin_examples/ios-example/GlutinExample/main.m delete mode 100644 glutin_examples/ios-example/Makefile delete mode 100644 glutin_examples/ios-example/README.md delete mode 100644 glutin_examples/ios-example/rust/.gitignore delete mode 100644 glutin_examples/ios-example/rust/Cargo.toml delete mode 100644 glutin_examples/ios-example/rust/Makefile delete mode 100644 glutin_examples/ios-example/rust/build.rs delete mode 100644 glutin_examples/ios-example/rust/glutin_ios_example.h delete mode 100644 glutin_examples/ios-example/rust/rust_logo.png delete mode 100644 glutin_examples/ios-example/rust/src/lib.rs diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f298c6fe85..44d1d65e13 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,4 @@ - [ ] Tested on all platforms changed -- [ ] Compilation warnings were addressed -- [ ] `cargo fmt` has been run on this branch -- [ ] `cargo doc` builds successfully - [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior - [ ] Created or updated an example program if it would help users understand this functionality diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ef58b857c..db150400c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: - uses: actions/checkout@v1 - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: nightly components: rustfmt - name: Check Formatting - run: cargo +stable fmt --all -- --check + run: cargo +nightly fmt --all -- --check tests: name: Tests @@ -35,30 +35,27 @@ jobs: platform: - { target: x86_64-pc-windows-msvc, os: windows-latest, } - { target: i686-pc-windows-msvc, os: windows-latest, } + - { target: i686-pc-windows-msvc, os: windows-latest, options: --no-default-features, features: wgl } + - { target: i686-pc-windows-msvc, os: windows-latest, options: --no-default-features, features: egl } - { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu } - { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu } - { target: i686-unknown-linux-gnu, os: ubuntu-latest, } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, } - - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 } - - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: wayland } - - { target: aarch64-linux-android, os: ubuntu-latest, } - - { target: arm-linux-androideabi, os: ubuntu-latest, } + - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "egl,wayland,x11" } + - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: glx } + - { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' } - { target: x86_64-apple-darwin, os: macos-latest, } - - { target: x86_64-apple-ios, os: macos-latest, } - - { target: aarch64-apple-ios, os: macos-latest, } - # We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web - # doesn't currently work on Linux. - #- { target: wasm32-unknown-unknown, os: windows-latest, features: stdweb, web: web } - #- { target: wasm32-unknown-unknown, os: windows-latest, features: web-sys, web: web } + # We don't support ios for now. + # - { target: x86_64-apple-ios, os: macos-latest, } + # - { target: aarch64-apple-ios, os: macos-latest, } env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 - PKG_CONFIG_ALLOW_CROSS: 1 RUSTFLAGS: "-C debuginfo=0" OPTIONS: ${{ matrix.platform.options }} + CMD: ${{ matrix.platform.cmd }} FEATURES: ${{ format(',{0}', matrix.platform.features ) }} - WEB: ${{ matrix.platform.web }} RUSTDOCFLAGS: -Dwarnings runs-on: ${{ matrix.platform.os }} @@ -82,58 +79,33 @@ jobs: # "Temporary" workaround until https://github.com/actions/virtual-environments/issues/5879#issuecomment-1195156618 # gets looked into. run: echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV - - name: Install Linux dependencies - if: (matrix.platform.os == 'ubuntu-latest') - run: sudo apt-get update && sudo apt-get install pkg-config cmake libfreetype6-dev libfontconfig1-dev libxkbcommon-dev + # We need those for examples. + - name: Install GCC Multilib if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') - run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install g++-multilib gcc-multilib libfreetype6-dev:i386 libfontconfig1-dev:i386 libxkbcommon-dev:i386 - - name: Install cargo-web - continue-on-error: true - if: contains(matrix.platform.target, 'wasm32') - run: cargo install cargo-web - - - name: Check documentation - shell: bash - if: matrix.platform.target != 'wasm32-unknown-unknown' - run: cd glutin && cargo doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items - - - name: Lint with clippy - shell: bash - if: matrix.rust_version == '1.57.0' - run: cargo clippy --workspace --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings + run: sudo apt-get update && sudo apt-get install gcc-multilib - - name: Build glutin - shell: bash - run: cd glutin && cargo $WEB build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - - name: Build all - shell: bash - if: (!contains(matrix.platform.target, 'wasm32')) - run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} + - name: Install cargo-apk + if: contains(matrix.platform.target, 'android') + run: cargo install cargo-apk - name: Build tests shell: bash - if: (!contains(matrix.platform.target, 'android')) - run: cd glutin && cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - - name: Build tests all - shell: bash - if: (!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'wasm32')) - run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} + run: cargo $CMD test -p glutin --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - name: Run tests shell: bash - if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'wasm32')) - run: cd glutin && cargo $WEB test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES + if: ( + !contains(matrix.platform.target, 'android') && + !contains(matrix.platform.target, 'ios') && + !contains(matrix.platform.target, 'wasm32')) + run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - - name: Build with serde enabled + - name: Check documentation shell: bash - if: (!contains(matrix.platform.target, 'android')) - run: cd glutin && cargo $WEB build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES + run: cd glutin && cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items - - name: Build tests with serde enabled - shell: bash - if: (!contains(matrix.platform.target, 'android')) - run: cd glutin && cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES - - name: Run tests with serde enabled + - name: Lint with clippy shell: bash - if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'wasm32')) - run: cd glutin && cargo $WEB test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES + if: (matrix.rust_version == '1.57.0') && !contains(matrix.platform.options, '--no-default-features') + run: cargo clippy --workspace --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings + diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c2cfd7f2..230ba3f011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Unreleased +- Replace `winit` dependency with `raw-window-handle`. +- The Api is now built around `Display`, `Surface`, `Config`, and `Surface` for more info see crate documentation and examples. +- The ios support was removed for the lack of maintainance for now. In case there's a need for it, contributions are welcome. +- The context creation is no longer limited to winit's supported platforms. +- The underlying Api providers are publically exposed now, so glutin could be used with just e.g. `EGL`. +- Fixed soundness issues with `Surface` MT safety, since before `EGLSurface` could be sent to a different thread, wich is not safe. + # Version 0.29.1 (2022-08-10) - Fix build failures when building from crates.io diff --git a/Cargo.toml b/Cargo.toml index 6dcac0b29a..9e4bee3e88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "glutin", "glutin_examples", - "glutin_examples/ios-example/rust", "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", diff --git a/ISSUES.md b/ISSUES.md deleted file mode 100644 index 0077b4e7ef..0000000000 --- a/ISSUES.md +++ /dev/null @@ -1,43 +0,0 @@ -# Common Issues - -## `NoAvailablePixelFormat` - -By far, the most common issue I receive every week is about receiving -`NoAvailablePixelFormat`, it is after all a fairly opaque error. - -So what does this error mean? Well, to quote @tomaka: - -> Glutin queries the system for the configuration you request (multisampling, -sRGB, depth buffer size, etc.) and if the system doesn't support this -configuration, then `NoAvailablePixelFormat` is returned. - -### Debugging on Linux. - -On Linux, like on other platforms, debugging this issue is relatively -straightforward. First, we need to know what configs are supported by your -hardware, and what that hardware is. This is trivial, just type the following -into your terminal: - -``` -$ lspci; glxinfo; glinfo; eglinfo -``` - -Next, track down what attributes glutin is passing to -`eglChooseConfig`/`glXChooseFBConfig`, and compare that to the attributes of the -configs available. - -### Debugging on Windows. - -Similar to linux, you are going to want to figure out what attributes glutin is -requesting from `ChoosePixelFormatARB`/`ChoosePixelFormat`/`eglChooseConfig`, -however figuring out what to compare them to is less trivial. - -The simplest solution I know of is to download the [OpenGL Extension Viewer by -realtech](http://realtech-vr.com/home/glview). You can then go to the "Display -modes & pixel formats" tab and take a look at all the configs supported by WGL, -I believe? I'm not what the equivalent for EGL is, unfortunately. - -It should be noted that if you are on Windows and have an AMD gpu, make sure you -are not requesting both a non-sRGB and non-floating point surface, else you -shall be shit out of luck. See: -https://github.com/rust-windowing/glutin/issues/1219 diff --git a/README.md b/README.md index 834ee3751b..450d30e884 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # glutin - OpenGL, UTilities and INput -A low-level library for OpenGL context creation, written in pure Rust. + +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) @@ -20,10 +21,11 @@ Join us in any of these: ## Usage Examples -Warning: these are examples for master. For the latest released version you can +**Warning:** these are examples for master. For the latest released version you can find them [here](https://github.com/rust-windowing/glutin/releases/tag/v0.29.1). -The examples use [gl_generator](https://crates.io/crates/gl_generator) to generate OpenGL bindings. +The examples use [gl_generator](https://crates.io/crates/gl_generator) to +generate OpenGL bindings. ### Try it! @@ -33,19 +35,18 @@ cd glutin cargo run --example window ``` -## Common issues - -Please refer to [ISSUES.md.](ISSUES.md) - ### Usage -Glutin is an OpenGL context creation library and doesn't directly provide OpenGL bindings for you. +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. +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 is only officially supported on the latest stable version of the Rust compiler. +The minimum rust version target by glutin is `1.57.0`. ## Platform-specific notes @@ -54,11 +55,3 @@ Glutin is only officially supported on the latest stable version of the Rust com To compile the examples for android, you have to use the `cargo apk` utility. See [`cargo-apk` in the `android-ndk-rs` repository](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk) for instructions. - -### X11 - -The plan is that glutin tries to dynamically link-to and use Wayland w/EGL if possible. If it doesn't work, it will try Xlib w/GLX follow by Xlib w/EGL instead. This is work-in-progress. - -### Wayland - -Due to an issue with how Mesa and Wayland play together, all shared contexts must use the same events pool as each other. diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index 626f76a26b..85e91bb53d 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -3,66 +3,57 @@ name = "glutin" version = "0.29.1" authors = ["The glutin contributors", "Pierre Krieger "] description = "Cross-platform OpenGL context provider." -keywords = ["windowing", "opengl"] +keywords = ["windowing", "opengl", "egl", "glx", "wgl", "cgl"] license = "Apache-2.0" readme = "../README.md" repository = "https://github.com/rust-windowing/glutin" documentation = "https://docs.rs/glutin" +rust-version = "1.57.0" edition = "2021" -rust-version = "1.57" - -[package.metadata.docs.rs] -features = ["serde"] [features] -default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] -serde = ["winit/serde"] -x11 = ["winit/x11", "glutin_glx_sys"] -wayland = ["winit/wayland", "wayland-client", "wayland-egl"] -wayland-dlopen = ["winit/wayland-dlopen"] -wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] -wayland-csd-adwaita-notitle = ["winit/wayland-csd-adwaita-notitle"] +default = ["egl", "glx", "x11", "wayland", "wgl"] +egl = ["glutin_egl_sys", "libloading"] +glx = ["x11", "x11-dl", "glutin_glx_sys", "libloading"] +wgl = ["glutin_wgl_sys", "windows-sys"] +x11 = ["x11-dl"] +wayland = ["wayland-sys"] [dependencies] +bitflags = "1.3.2" +libloading = { version = "0.7.3", optional = true } once_cell = "1.13" -winit = { version = "0.27.1", default-features = false } - -[target.'cfg(target_os = "android")'.dependencies] -glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys" } -libloading = "0.7" -parking_lot = "0.12" -raw-window-handle = "0.5" - -[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] -objc = "0.2.6" -glutin_gles2_sys = { version = "0.1.5", path = "../glutin_gles2_sys" } +raw-window-handle = "0.5.0" -[target.'cfg(target_os = "macos")'.dependencies] -cgl = "0.3" -cocoa = "0.24" -core-foundation = "0.9" +[target.'cfg(target_os = "windows")'.dependencies] +glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys", optional = true } +glutin_wgl_sys = { version = "0.1.5", path = "../glutin_wgl_sys", optional = true } -[target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3" +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = "0.36" features = [ - "winnt", - "winuser", - "wingdi", - "libloaderapi", + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_Graphics_OpenGL", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", ] +optional = true -[target.'cfg(target_os = "windows")'.dependencies] -libloading = "0.7" -glutin_wgl_sys = { version = "0.1.5", path = "../glutin_wgl_sys" } +[target.'cfg(target_os = "android")'.dependencies] glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys" } -parking_lot = "0.12" [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] -osmesa-sys = "0.1" -wayland-client = { version = "0.29.4", features = ["dlopen"], optional = true } -wayland-egl = { version = "0.29.4", optional = true } -libloading = "0.7" -glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys" } +glutin_egl_sys = { version = "0.1.6", path = "../glutin_egl_sys", optional = true } glutin_glx_sys = { version = "0.1.8", path = "../glutin_glx_sys", optional = true } -parking_lot = "0.12" -log = "0.4" +wayland-sys = { version = "0.30.0-beta.8", 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" +cocoa = "0.24.0" +core-foundation = "0.9.3" +objc = "0.2.7" + +[build-dependencies] +cfg_aliases = "0.1.1" diff --git a/glutin/build.rs b/glutin/build.rs new file mode 100644 index 0000000000..b542c07a9c --- /dev/null +++ b/glutin/build.rs @@ -0,0 +1,24 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // Setup alias to reduce `cfg` boilerplate. + cfg_aliases! { + // Systems. + android: { target_os = "android" }, + wasm: { target_arch = "wasm32" }, + macos: { target_os = "macos" }, + ios: { target_os = "ios" }, + apple: { any(target_os = "ios", target_os = "macos") }, + free_unix: { all(unix, not(apple), not(android)) }, + + // Native displays. + x11_platform: { all(feature = "x11", free_unix, not(wasm)) }, + wayland_platform: { all(feature = "wayland", free_unix, not(wasm)) }, + + // Backends. + egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm)) }, + glx_backend: { all(feature = "glx", x11_platform, not(wasm)) }, + wgl_backend: { all(feature = "wgl", windows, not(wasm)) }, + cgl_backend: { all(macos, not(wasm)) }, + } +} diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs new file mode 100644 index 0000000000..4d5a8db5b0 --- /dev/null +++ b/glutin/src/api/cgl/config.rs @@ -0,0 +1,230 @@ +//! Everything related to `NSOpenGLPixelFormat`. + +use std::ops::Deref; +use std::sync::Arc; +use std::{fmt, iter}; + +use cocoa::appkit::{NSOpenGLPixelFormat, NSOpenGLPixelFormatAttribute}; +use cocoa::base::{id, nil, BOOL}; + +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::display::Display; + +impl Display { + 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(NSOpenGLPixelFormatAttribute::NSOpenGLPFAMinimumPolicy as u32); + + // Color. + match template.color_buffer_type { + ColorBufferType::Rgb { r_size, g_size, b_size } => { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAColorSize as u32); + // 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(NSOpenGLPixelFormatAttribute::NSOpenGLPFAAlphaSize as u32); + attrs.push(template.alpha_size as u32); + + // Depth. + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFADepthSize as u32); + attrs.push(template.depth_size as u32); + + // Stencil. + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAStencilSize as u32); + attrs.push(template.stencil_size as u32); + + // Float colors. + if template.float_pixels { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAColorFloat as u32); + } + + // Sample buffers. + if template.sample_buffers != 0 { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAMultisample as u32); + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFASampleBuffers as u32); + attrs.push(template.sample_buffers as u32); + } + + // Double buffering. + if !template.single_buffering { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFADoubleBuffer as u32); + } + + // Stereo. + if template.stereoscopy == Some(true) { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAStereo as u32); + } + + // Terminate attrs with zero. + attrs.push(0); + + let raw = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs); + if raw.is_null() { + return Err(ErrorKind::BadConfig.into()); + } + + let inner = Arc::new(ConfigInner { + display: self.clone(), + raw: NSOpenGLPixelFormatId(raw), + transrarency: 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; + NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( + *self.inner.raw, + &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 as i32 + } + } + + pub(crate) fn is_single_buffered(&self) -> bool { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFATripleBuffer) == 0 + && self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFADoubleBuffer) == 0 + } +} + +impl GlConfig for Config { + fn color_buffer_type(&self) -> ColorBufferType { + // 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(NSOpenGLPixelFormatAttribute::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; + ColorBufferType::Rgb { r_size, g_size, b_size } + } + + fn float_pixels(&self) -> bool { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFAColorFloat) != 0 + } + + fn alpha_size(&self) -> u8 { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFAAlphaSize) as u8 + } + + fn srgb_capable(&self) -> bool { + true + } + + fn depth_size(&self) -> u8 { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFADepthSize) as u8 + } + + fn stencil_size(&self) -> u8 { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFAStencilSize) as u8 + } + + fn sample_buffers(&self) -> u8 { + self.raw_attribute(NSOpenGLPixelFormatAttribute::NSOpenGLPFASampleBuffers) as u8 + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + ConfigSurfaceTypes::WINDOW + } + + 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(self.inner.raw.cast()) + } +} + +impl Sealed for Config {} + +pub(crate) struct ConfigInner { + display: Display, + pub(crate) transrarency: bool, + pub(crate) raw: NSOpenGLPixelFormatId, +} + +impl Drop for ConfigInner { + fn drop(&mut self) { + if *self.raw != nil { + let _: () = unsafe { msg_send![*self.raw, release] }; + } + } +} + +impl PartialEq for ConfigInner { + fn eq(&self, other: &Self) -> bool { + unsafe { + let is_equal: BOOL = msg_send![*self.raw, isEqual: *other.raw]; + is_equal != 0 + } + } +} + +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() + } +} + +#[derive(Debug)] +pub(crate) struct NSOpenGLPixelFormatId(id); + +unsafe impl Send for NSOpenGLPixelFormatId {} +unsafe impl Sync for NSOpenGLPixelFormatId {} + +impl Deref for NSOpenGLPixelFormatId { + type Target = id; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/glutin/src/api/cgl/context.rs b/glutin/src/api/cgl/context.rs new file mode 100644 index 0000000000..8b2ee65e88 --- /dev/null +++ b/glutin/src/api/cgl/context.rs @@ -0,0 +1,298 @@ +//! Everything related to `NSOpenGLContext`. + +use std::ffi::{self, CStr}; +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; + +use cgl::CGLSetParameter; +use cocoa::appkit::{NSOpenGLContext, NSOpenGLContextParameter}; +use cocoa::base::{id, nil}; +use core_foundation::base::TCFType; +use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; +use core_foundation::string::CFString; +use objc::rc::autoreleasepool; +use objc::runtime::{BOOL, NO}; + +use crate::config::GetGlConfig; +use crate::context::{AsRawContext, ContextAttributes, 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::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)) => share_context.cast(), + _ => nil, + }; + + if context_attributes.robustness != Robustness::NotRobust { + return Err(ErrorKind::NotSupported("robustness is not supported with CGL").into()); + } + + let config = config.clone(); + let raw = NSOpenGLContext::alloc(nil) + .initWithFormat_shareContext_(*config.inner.raw, share_context as *mut _); + + if config.inner.transrarency { + let opacity = 0; + super::check_error(CGLSetParameter( + raw.CGLContextObj().cast(), + cgl::kCGLCPSurfaceOpacity, + &opacity, + ))?; + } + + let inner = ContextInner { display: self.clone(), config, raw: NSOpenGLContextId(raw) }; + let context = NotCurrentContext { 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, +} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; + + 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 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(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, +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn is_current(&self) -> bool { + autoreleasepool(|| unsafe { + let current = NSOpenGLContext::currentContext(nil); + if current != nil { + let is_equal: BOOL = msg_send![current, isEqual: *self.inner.raw]; + is_equal != NO + } else { + false + } + }) + } + + 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() + } + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + 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 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(self.inner.raw.cast()) + } +} + +impl Sealed for PossiblyCurrentContext {} + +pub(crate) struct ContextInner { + display: Display, + config: Config, + pub(crate) raw: NSOpenGLContextId, +} + +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(|| unsafe { + self.raw.update(); + self.raw.makeCurrentContext(); + self.raw.setView_(surface.ns_view); + Ok(()) + }) + } + + 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, + NSOpenGLContextParameter::NSOpenGLCPSwapInterval, + ); + }) + } + + pub(crate) fn update(&self) { + unsafe { self.raw.update() } + } + + pub(crate) fn flush_buffer(&self) -> Result<()> { + autoreleasepool(|| unsafe { + self.raw.flushBuffer(); + Ok(()) + }) + } + + pub(crate) fn current_view(&self) -> id { + unsafe { self.raw.view() } + } + + fn make_not_current(&self) -> Result<()> { + unsafe { + self.raw.update(); + NSOpenGLContext::clearCurrentContext(nil); + Ok(()) + } + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + unsafe { + if *self.raw != nil { + let _: () = msg_send![*self.raw, release]; + } + } + } +} + +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() + } +} + +#[derive(Debug)] +pub(crate) struct NSOpenGLContextId(id); + +unsafe impl Send for NSOpenGLContextId {} + +impl Deref for NSOpenGLContextId { + type Target = id; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/glutin/src/api/cgl/display.rs b/glutin/src/api/cgl/display.rs new file mode 100644 index 0000000000..0674cb4744 --- /dev/null +++ b/glutin/src/api/cgl/display.rs @@ -0,0 +1,88 @@ +//! A CGL display. + +use std::marker::PhantomData; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; + +/// The CGL display. +#[derive(Debug, Clone)] +pub struct Display { + // Prevent building of it without constructor. + _marker: PhantomData<()>, +} + +impl Display { + /// Create CGL display. + pub fn from_raw(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 + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + unsafe fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + unsafe fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Cgl + } +} + +impl Sealed for Display {} diff --git a/glutin/src/api/cgl/mod.rs b/glutin/src/api/cgl/mod.rs new file mode 100644 index 0000000000..99e2903c56 --- /dev/null +++ b/glutin/src/api/cgl/mod.rs @@ -0,0 +1,62 @@ +//! The CGL Api. + +#![allow(non_upper_case_globals)] + +use std::ffi::CStr; +use std::os::raw::c_int; + +use cgl::{kCGLNoError, CGLError, CGLErrorString}; + +use crate::error::{Error, ErrorKind, Result}; + +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)) +} diff --git a/glutin/src/api/cgl/surface.rs b/glutin/src/api/cgl/surface.rs new file mode 100644 index 0000000000..8ee43f7d9d --- /dev/null +++ b/glutin/src/api/cgl/surface.rs @@ -0,0 +1,158 @@ +//! Wrapper around `NSView`. + +use std::fmt; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +use cocoa::base::{id, nil}; +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) 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() + ) + }, + }; + + let ns_view: id = native_window.ns_view.cast(); + let _: () = msg_send![ns_view, retain]; + let surface = + Surface { display: self.clone(), config: config.clone(), ns_view, _ty: PhantomData }; + Ok(surface) + } +} + +/// A wrapper aroud `NSView`. +pub struct Surface { + display: Display, + config: Config, + pub(crate) ns_view: id, + _ty: PhantomData, +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + if self.ns_view != nil { + let _: () = msg_send![self.ns_view, retain]; + } + } + } +} + +impl GlSurface for Surface { + type Context = PossiblyCurrentContext; + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + 0 + } + + fn width(&self) -> Option { + None + } + + fn height(&self) -> Option { + None + } + + 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 { + self.ns_view == context.inner.current_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 { + RawSurface::Cgl(self.ns_view.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 {} diff --git a/glutin/src/api/dlloader.rs b/glutin/src/api/dlloader.rs deleted file mode 100644 index 40bbb414e2..0000000000 --- a/glutin/src/api/dlloader.rs +++ /dev/null @@ -1,66 +0,0 @@ -#![cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", -))] - -use libloading::Library; - -#[cfg(target_os = "windows")] -use libloading::os::windows; - -#[cfg(target_os = "windows")] -use winapi::um::libloaderapi::*; - -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -#[derive(Clone)] -pub struct SymWrapper { - inner: T, - _lib: Arc, -} - -pub trait SymTrait { - fn load_with(lib: &Library) -> Self; -} - -impl SymWrapper { - pub fn new(lib_paths: Vec<&str>) -> Result { - for path in lib_paths { - // Avoid loading from PATH - #[cfg(target_os = "windows")] - let lib = unsafe { - windows::Library::load_with_flags(path, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) - .map(From::from) - }; - - #[cfg(not(target_os = "windows"))] - let lib = unsafe { Library::new(path) }; - - if let Ok(lib) = lib { - return Ok(SymWrapper { inner: T::load_with(&lib), _lib: Arc::new(lib) }); - } - } - - Err(()) - } -} - -impl Deref for SymWrapper { - type Target = T; - - fn deref(&self) -> &T { - &self.inner - } -} - -impl DerefMut for SymWrapper { - fn deref_mut(&mut self) -> &mut T { - &mut self.inner - } -} diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs new file mode 100644 index 0000000000..ed7200eda6 --- /dev/null +++ b/glutin/src/api/egl/config.rs @@ -0,0 +1,377 @@ +//! Everything related to finding and manipulating the `EGLConfig`. + +use std::ops::Deref; +use std::sync::Arc; +use std::{fmt, mem}; + +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::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; + +const FLOAT_PIXELS_EXT: &str = "EGL_EXT_pixel_format_float"; +const SRGB_SURFACE: &str = "EGL_KHR_gl_colorspace"; + +impl Display { + pub(crate) 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.client_extensions.contains(FLOAT_PIXELS_EXT) { + 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 minimum swap interval. + if let Some(min_swap_interval) = template.min_swap_interval { + config_attributes.push(egl::MIN_SWAP_INTERVAL as EGLint); + config_attributes.push(min_swap_interval as EGLint) + } + + // Add maximum swap interval. + if let Some(max_swap_interval) = template.max_swap_interval { + config_attributes.push(egl::MAX_SWAP_INTERVAL as EGLint); + config_attributes.push(max_swap_interval as EGLint) + } + + // Add samples. + config_attributes.push(egl::SAMPLE_BUFFERS as EGLint); + config_attributes.push(template.sample_buffers as EGLint); + + let mut api = 0; + if template.api.contains(Api::GLES1) { + api |= egl::OPENGL_ES_API; + } + if template.api.contains(Api::GLES2) { + api |= egl::OPENGL_ES2_BIT; + } + if template.api.contains(Api::GLES3) { + api |= egl::OPENGL_ES3_BIT; + } + if template.api.contains(Api::OPENGL) { + api |= egl::OPENGL_BIT; + } + config_attributes.push(egl::RENDERABLE_TYPE as EGLint); + config_attributes.push(api as EGLint); + + // Add maximum height of pbuffer. + if let Some(pbuffer_width) = template.max_pbuffer_width { + config_attributes.push(egl::MAX_PBUFFER_WIDTH as EGLint); + config_attributes.push(pbuffer_width as EGLint); + } + + // Add maximum width of pbuffer. + if let Some(pbuffer_height) = template.max_pbuffer_height { + config_attributes.push(egl::MAX_PBUFFER_HEIGHT as EGLint); + config_attributes.push(pbuffer_height as EGLint); + } + + // Push `egl::NONE` to terminate the list. + config_attributes.push(egl::NONE as EGLint); + + let mut configs_number = self.configs_number() as EGLint; + let mut found_configs: Vec = + unsafe { vec![mem::zeroed(); configs_number as usize] }; + + unsafe { + let result = self.inner.egl.ChooseConfig( + *self.inner.raw, + config_attributes.as_ptr(), + found_configs.as_mut_ptr(), + configs_number as EGLint, + &mut configs_number, + ); + + if result == egl::FALSE { + return 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| { + #[cfg(x11_platform)] + if template.transparency { + if let raw_window_handle::RawDisplayHandle::Xlib(display_handle) = + *self.inner._native_display + { + let xid = _config.native_visual(); + let xid = unsafe { + X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) + }; + return xid.map_or(false, |xid| xid.supports_transparency()); + } + } + + true + }); + + 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 { + self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 + } + + 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) -> ColorBufferType { + match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ { + egl::LUMINANCE_BUFFER => { + let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint); + ColorBufferType::Luminance(luma as u8) + }, + egl::RGB_BUFFER => { + let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8; + let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8; + let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8; + ColorBufferType::Rgb { r_size, g_size, b_size } + }, + _ => unreachable!(), + } + } + + fn float_pixels(&self) -> bool { + if self.inner.display.inner.client_extensions.contains(FLOAT_PIXELS_EXT) { + 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 { + self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 + } + + fn srgb_capable(&self) -> bool { + self.inner.display.inner.client_extensions.contains(SRGB_SURFACE) + } + + fn depth_size(&self) -> u8 { + self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 + } + + fn stencil_size(&self) -> u8 { + self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 + } + + fn sample_buffers(&self) -> u8 { + self.raw_attribute(egl::SAMPLE_BUFFERS as EGLint) as u8 + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + let mut ty = ConfigSurfaceTypes::empty(); + + let raw_ty = self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32; + if raw_ty & egl::WINDOW_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::WINDOW); + } + if raw_ty & egl::PBUFFER_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PBUFFER); + } + if raw_ty & egl::PIXMAP_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PIXMAP); + } + + ty + } + + fn api(&self) -> Api { + let mut api = Api::empty(); + let raw_api = self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32; + if raw_api & egl::OPENGL_BIT as u32 != 0 { + api.insert(Api::OPENGL); + } + if raw_api & egl::OPENGL_ES_BIT as u32 != 0 { + api.insert(Api::GLES1); + } + if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 { + api.insert(Api::GLES2); + } + if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 { + api.insert(Api::GLES3); + } + + api + } +} + +impl GetGlDisplay for Config { + type Target = Display; + + fn display(&self) -> Self::Target { + Display { inner: self.inner.display.inner.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 *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 + } +} diff --git a/glutin/src/api/egl/context.rs b/glutin/src/api/egl/context.rs new file mode 100644 index 0000000000..67c455cf96 --- /dev/null +++ b/glutin/src/api/egl/context.rs @@ -0,0 +1,343 @@ +//! Everything related to `EGLContext` management. + +use std::ffi::{self, CStr}; +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; + +use glutin_egl_sys::egl::types::EGLint; +use glutin_egl_sys::{egl, EGLContext}; + +use crate::config::GetGlConfig; +use crate::context::{ + AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, Robustness, Version, +}; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let mut attrs = Vec::::new(); + + let supports_opengl = self.inner.version > Version::new(1, 3); + + let (api, version) = match context_attributes.api { + ContextApi::OpenGl(version) if supports_opengl => (egl::OPENGL_API, version), + ContextApi::Gles(version) => (egl::OPENGL_ES_API, 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.client_extensions.contains("EGL_KHR_create_context") { + let mut flags = 0; + + // Add profile for the OpenGL Api. + if api == egl::OPENGL_API { + let profile = match context_attributes.profile { + Some(GlProfile::Core) | None => egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, + Some(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 = is_one_five + || self.inner.client_extensions.contains("EGL_EXT_create_context_robustness"); + let has_no_error = + self.inner.client_extensions.contains("EGL_KHR_create_context_no_error"); + + match context_attributes.robustness { + Robustness::NotRobust => (), + Robustness::NoError if has_no_error => { + attrs.push(egl::CONTEXT_OPENGL_NO_ERROR_KHR as EGLint); + attrs.push(egl::TRUE as EGLint); + }, + Robustness::RobustLoseContextOnReset if has_robustsess => { + attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); + attrs.push(egl::LOSE_CONTEXT_ON_RESET as EGLint); + flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS; + }, + Robustness::RobustNoResetNotification if has_robustsess => { + attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint); + attrs.push(egl::NO_RESET_NOTIFICATION as EGLint); + flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS; + }, + _ => { + return Err( + ErrorKind::NotSupported("context robustness is not supported").into() + ) + }, + } + + if context_attributes.debug && is_one_five && !has_no_error { + attrs.push(egl::CONTEXT_OPENGL_DEBUG as EGLint); + attrs.push(egl::TRUE as EGLint); + } + + if flags != 0 { + attrs.push(egl::CONTEXT_FLAGS_KHR as EGLint); + attrs.push(flags as EGLint); + } + } + + attrs.push(egl::NONE as EGLint); + + let shared_context = if let Some(shared_context) = + context_attributes.shared_context.as_ref() + { + match shared_context { + RawContext::Egl(shared_context) => *shared_context, + #[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) }; + Ok(NotCurrentContext::new(inner)) + } + } +} + +/// A wrapper around `EGLContext` that is known to be not current. +#[derive(Debug)] +pub struct NotCurrentContext { + inner: ContextInner, +} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> Self::PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; + + 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 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 { + inner: ContextInner, + _nosendsync: PhantomData, +} + +impl PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn is_current(&self) -> bool { + unsafe { self.inner.display.inner.egl.GetCurrentContext() == *self.inner.raw } + } + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { self.inner.display.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ } + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl 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 {} + +struct ContextInner { + display: Display, + config: Config, + raw: EglContext, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + unsafe { + let draw = surface_draw.raw; + let read = surface_read.raw; + if self.display.inner.egl.MakeCurrent(*self.display.inner.raw, draw, read, *self.raw) + == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + fn make_not_current(&self) -> Result<()> { + unsafe { + if self.display.inner.egl.MakeCurrent( + *self.display.inner.raw, + egl::NO_SURFACE, + egl::NO_SURFACE, + egl::NO_CONTEXT, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } +} + +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 + } +} diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs new file mode 100644 index 0000000000..825429188a --- /dev/null +++ b/glutin/src/api/egl/display.rs @@ -0,0 +1,349 @@ +//! Everything related to `EGLDisplay`. + +use std::collections::HashSet; +use std::ffi::CStr; +use std::fmt; +use std::ops::Deref; +use std::sync::Arc; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; + +use once_cell::sync::OnceCell; + +use raw_window_handle::RawDisplayHandle; + +use crate::config::ConfigTemplate; +use crate::context::Version; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; + +use super::{Egl, EGL}; + +/// Extensions that don't require any display. +static NO_DISPLAY_EXTENSIONS: OnceCell> = OnceCell::new(); + +/// 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 platfrom with a concept of native display, like Wayland. + pub unsafe fn from_raw(raw_display: RawDisplayHandle) -> Result { + let egl = match EGL.as_ref() { + Some(egl) => egl, + None => return Err(ErrorKind::NotFound.into()), + }; + + NO_DISPLAY_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); + + // Create a EGL display by chaining all display creation functions aborting on + // `EGL_BAD_ATTRIBUTE`. + let display = Self::get_platform_display(egl, raw_display) + .or_else(|err| { + if err.error_kind() == ErrorKind::BadAttribute { + Err(err) + } else { + Self::get_platform_display_ext(egl, raw_display) + } + }) + .or_else(|err| { + if err.error_kind() == ErrorKind::BadAttribute { + Err(err) + } else { + Self::get_display(egl, raw_display) + } + })?; + + let (mut major, mut minor) = (0, 0); + if egl.Initialize(display, &mut major, &mut minor) == egl::FALSE { + return Err(super::check_error().err().unwrap()); + } + + let version = Version::new(major as u8, minor as u8); + + // Load extensions. + let client_extensions = get_extensions(egl, display); + + let inner = Arc::new(DisplayInner { + egl, + raw: EglDisplay(display), + _native_display: NativeDisplay(raw_display), + version, + client_extensions, + }); + Ok(Self { inner }) + } + + fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result { + if !egl.GetPlatformDisplay.is_loaded() { + return Err(ErrorKind::NotSupported("eglGetPlatformDisplay is not supported").into()); + } + + let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + let mut attrs = Vec::::new(); + let (platform, mut display) = match display { + #[cfg(wayland_platform)] + RawDisplayHandle::Wayland(handle) + if extensions.contains("EGL_KHR_platform_wayland") => + { + (egl::PLATFORM_WAYLAND_KHR, handle.display) + }, + #[cfg(x11_platform)] + RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => { + attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib); + attrs.push(handle.screen as EGLAttrib); + (egl::PLATFORM_X11_KHR, handle.display) + }, + RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => { + (egl::PLATFORM_GBM_KHR, handle.gbm_device) + }, + RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => { + (egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _) + }, + _ => { + return Err( + ErrorKind::NotSupported("provided display handle is not supported").into() + ) + }, + }; + + // Be explicit here. + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + let display = + unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) }; + + Self::check_display_error(display) + } + + fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result { + if !egl.GetPlatformDisplayEXT.is_loaded() { + return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into()); + } + + let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + let mut attrs = Vec::::new(); + let (platform, mut display) = match display { + #[cfg(wayland_platform)] + RawDisplayHandle::Wayland(handle) + if extensions.contains("EGL_EXT_platform_wayland") => + { + (egl::PLATFORM_WAYLAND_EXT, handle.display) + }, + #[cfg(x11_platform)] + 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) + }, + #[cfg(x11_platform)] + RawDisplayHandle::Xcb(handle) if extensions.contains("EGL_MESA_platform_xcb") => { + attrs.push(egl::PLATFORM_XCB_EXT as EGLint); + attrs.push(handle.screen as EGLint); + (egl::PLATFORM_XCB_EXT, handle.connection) + }, + RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm") => { + (egl::PLATFORM_GBM_MESA, handle.gbm_device) + }, + _ => { + return Err( + ErrorKind::NotSupported("provided display handle is not supported").into() + ) + }, + }; + + // Be explicit here. + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLint); + + let display = + unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) }; + + Self::check_display_error(display) + } + + fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result { + let mut display = match display { + RawDisplayHandle::Gbm(handle) => handle.gbm_device, + #[cfg(x11_platform)] + RawDisplayHandle::Xlib(handle) => handle.display, + RawDisplayHandle::Android(_) => egl::DEFAULT_DISPLAY as *mut _, + _ => { + return Err( + ErrorKind::NotSupported("provided display handle is not supported").into() + ) + }, + }; + + if display.is_null() { + display = egl::DEFAULT_DISPLAY as *mut _; + } + + let display = unsafe { egl.GetDisplay(display) }; + Self::check_display_error(display) + } + + fn check_display_error(display: EGLDisplay) -> Result { + if display == egl::NO_DISPLAY { + Err(super::check_error().err().unwrap()) + } else { + Ok(display) + } + } +} + +impl GlDisplay for Display { + type Config = Config; + type NotCurrentContext = NotCurrentContext; + type PbufferSurface = Surface; + type PixmapSurface = Surface; + type WindowSurface = Surface; + + unsafe fn find_configs( + &self, + template: ConfigTemplate, + ) -> Result + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + unsafe fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + unsafe fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::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, + + /// Client EGL extensions. + pub(crate) client_extensions: HashSet<&'static str>, + + /// The raw display used to create EGL display. + pub(crate) _native_display: NativeDisplay, +} + +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("extensions", &self.client_extensions) + .finish() + } +} + +#[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) struct EglDisplay(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 { + &self.0 + } +} + +/// Collect EGL extensions for the given `display`. +fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { + unsafe { + let extensions = egl.QueryString(display, egl::EXTENSIONS as i32); + if extensions.is_null() { + return HashSet::new(); + } + + if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { + extensions.split(' ').collect::>() + } else { + HashSet::new() + } + } +} diff --git a/glutin/src/api/egl/make_current_guard.rs b/glutin/src/api/egl/make_current_guard.rs deleted file mode 100644 index ae078bd8e4..0000000000 --- a/glutin/src/api/egl/make_current_guard.rs +++ /dev/null @@ -1,98 +0,0 @@ -use glutin_egl_sys as ffi; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct MakeCurrentGuard { - display: ffi::egl::types::EGLDisplay, - old_display: ffi::egl::types::EGLDisplay, - possibly_invalid: Option, -} - -#[derive(Debug, PartialEq, Eq)] -struct MakeCurrentGuardInner { - old_draw_surface: ffi::egl::types::EGLSurface, - old_read_surface: ffi::egl::types::EGLSurface, - old_context: ffi::egl::types::EGLContext, -} - -impl MakeCurrentGuard { - pub fn new( - display: ffi::egl::types::EGLDisplay, - draw_surface: ffi::egl::types::EGLSurface, - read_surface: ffi::egl::types::EGLSurface, - context: ffi::egl::types::EGLContext, - ) -> Result { - unsafe { - let egl = super::EGL.as_ref().unwrap(); - - let mut ret = MakeCurrentGuard { - display, - old_display: egl.GetCurrentDisplay(), - possibly_invalid: Some(MakeCurrentGuardInner { - old_draw_surface: egl.GetCurrentSurface(ffi::egl::DRAW as i32), - old_read_surface: egl.GetCurrentSurface(ffi::egl::READ as i32), - old_context: egl.GetCurrentContext(), - }), - }; - - if ret.old_display == ffi::egl::NO_DISPLAY { - ret.invalidate(); - } - - let res = egl.MakeCurrent(display, draw_surface, read_surface, context); - - if res == 0 { - let err = egl.GetError(); - Err(format!("`eglMakeCurrent` failed: 0x{:x}", err)) - } else { - Ok(ret) - } - } - } - - pub fn if_any_same_then_invalidate( - &mut self, - draw_surface: ffi::egl::types::EGLSurface, - read_surface: ffi::egl::types::EGLSurface, - context: ffi::egl::types::EGLContext, - ) { - if self.possibly_invalid.is_some() { - let pi = self.possibly_invalid.as_ref().unwrap(); - if pi.old_draw_surface == draw_surface && draw_surface != ffi::egl::NO_SURFACE - || pi.old_read_surface == read_surface && read_surface != ffi::egl::NO_SURFACE - || pi.old_context == context - { - self.invalidate(); - } - } - } - - pub fn invalidate(&mut self) { - self.possibly_invalid.take(); - } -} - -impl Drop for MakeCurrentGuard { - fn drop(&mut self) { - let egl = super::EGL.as_ref().unwrap(); - let (draw_surface, read_surface, context) = match self.possibly_invalid.take() { - Some(inner) => (inner.old_draw_surface, inner.old_read_surface, inner.old_context), - None => (ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, ffi::egl::NO_CONTEXT), - }; - - let display = match self.old_display { - ffi::egl::NO_DISPLAY => self.display, - old_display => old_display, - }; - - unsafe { - let res = egl.MakeCurrent(display, draw_surface, read_surface, context); - - if res == 0 { - let err = egl.GetError(); - panic!("`eglMakeCurrent` failed: 0x{:x}", err) - } - } - } -} diff --git a/glutin/src/api/egl/mod.rs b/glutin/src/api/egl/mod.rs index 49e8660125..828c15d89c 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -1,104 +1,70 @@ -#![cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] +//! EGL platform Api. -use std::ffi::{CStr, CString}; +use std::ffi::{self, CString}; use std::ops::{Deref, DerefMut}; -use std::os::raw; -use glutin_egl_sys as ffi; -use libloading; +use glutin_egl_sys::egl; + +use libloading::Library; +use once_cell::sync::{Lazy, OnceCell}; + #[cfg(unix)] use libloading::os::unix as libloading_os; #[cfg(windows)] use libloading::os::windows as libloading_os; -use once_cell::sync::{Lazy, OnceCell}; -#[cfg(any( - target_os = "android", - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -use winit::dpi; -use self::make_current_guard::MakeCurrentGuard; -use crate::api::dlloader::{SymTrait, SymWrapper}; -#[cfg(not(target_os = "windows"))] -use crate::Rect; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; +use crate::error::{Error, ErrorKind, Result}; +use crate::lib_loading::{SymLoading, SymWrapper}; -#[derive(Clone)] -pub struct Egl(pub SymWrapper); +pub mod config; +pub mod context; +pub mod display; +pub mod surface; -/// Because `*const raw::c_void` doesn't implement `Sync`. -unsafe impl Sync for Egl {} +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"]; -type EglGetProcAddressType = libloading_os::Symbol< - unsafe extern "C" fn(*const std::os::raw::c_void) -> *const std::os::raw::c_void, ->; + SymWrapper::new(&paths).map(Egl).ok() +}); -static EGL_GET_PROC_ADDRESS: OnceCell = OnceCell::new(); +type EglGetProcAddress = unsafe extern "C" fn(*const ffi::c_void) -> *const ffi::c_void; +static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); -impl SymTrait for ffi::egl::Egl { - fn load_with(lib: &libloading::Library) -> Self { - let f = move |s: &'static str| -> *const std::os::raw::c_void { - let s = std::ffi::CString::new(s.as_bytes()).unwrap(); - let s = s.as_bytes_with_nul(); +pub(crate) struct Egl(pub SymWrapper); + +unsafe impl Sync for Egl {} +unsafe impl Send for Egl {} - // Check if the symbol is available in the library directly. If - // it is, just return it. - if let Ok(sym) = unsafe { lib.get(s) } { +impl SymLoading for egl::Egl { + unsafe fn load_with(lib: &Library) -> Self { + let loader = move |sym_name: &'static str| -> *const ffi::c_void { + let sym_name = CString::new(sym_name.as_bytes()).unwrap(); + if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { return *sym; } - let egl_get_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| unsafe { - let sym: libloading::Symbol< - unsafe extern "C" fn( - *const std::os::raw::c_void, - ) -> *const std::os::raw::c_void, - > = lib.get(b"eglGetProcAddress\0").unwrap(); + let egl_proc_address = EGL_GET_PROC_ADDRESS.get_or_init(|| { + 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. - unsafe { (egl_get_proc_address)(s.as_ptr().cast()) } + // The symbol was not available in the library, so ask eglGetProcAddress for it. + // Note that eglGetProcAddress was only able to look up extension + // functions prior to EGL 1.5, hence this two-part dance. + (egl_proc_address)(sym_name.as_bytes_with_nul().as_ptr() as *const ffi::c_void) }; - Self::load_with(f) + Self::load_with(loader) } } -impl Egl { - pub fn new() -> Result { - #[cfg(target_os = "windows")] - let paths = vec!["libEGL.dll", "atioglxx.dll"]; - - #[cfg(not(target_os = "windows"))] - let paths = vec!["libEGL.so.1", "libEGL.so"]; - - SymWrapper::new(paths).map(Egl) - } -} - -mod make_current_guard; - impl Deref for Egl { - type Target = ffi::egl::Egl; + type Target = egl::Egl; fn deref(&self) -> &Self::Target { &self.0 @@ -106,1180 +72,35 @@ impl Deref for Egl { } impl DerefMut for Egl { - fn deref_mut(&mut self) -> &mut ffi::egl::Egl { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -pub static EGL: Lazy> = Lazy::new(|| Egl::new().ok()); - -/// Specifies the type of display passed as `native_display`. -#[derive(Debug)] -#[allow(dead_code)] -pub enum NativeDisplay { - /// [`None`] means `EGL_DEFAULT_DISPLAY`. - X11(Option), - /// [`None`] means `EGL_DEFAULT_DISPLAY`. - Gbm(Option), - /// [`None`] means `EGL_DEFAULT_DISPLAY`. - Wayland(Option), - /// `EGL_DEFAULT_DISPLAY` is mandatory for Android. - Android, - // TODO: should be `EGLDeviceEXT` - Device(ffi::EGLNativeDisplayType), - /// Don't specify any display type. Useful on windows. [`None`] means - /// `EGL_DEFAULT_DISPLAY`. - Other(Option), -} - -#[derive(Debug)] -pub struct Context { - display: ffi::egl::types::EGLDisplay, - context: ffi::egl::types::EGLContext, - surface: Option>, - api: Api, - pixel_format: PixelFormat, -} - -fn get_egl_version( - display: ffi::egl::types::EGLDisplay, -) -> Result<(ffi::egl::types::EGLint, ffi::egl::types::EGLint), CreationError> { - unsafe { - let egl = EGL.as_ref().unwrap(); - let mut major: ffi::egl::types::EGLint = std::mem::zeroed(); - let mut minor: ffi::egl::types::EGLint = std::mem::zeroed(); - - if egl.Initialize(display, &mut major, &mut minor) == 0 { - return Err(CreationError::OsError("eglInitialize failed".to_string())); - } - - Ok((major, minor)) - } -} - -unsafe fn bind_and_get_api<'a>( - opengl: &'a GlAttributes<&'a Context>, - egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), -) -> Result<(Option<(u8, u8)>, Api), CreationError> { - let egl = EGL.as_ref().unwrap(); - match opengl.version { - GlRequest::Latest => { - if egl_version >= (1, 4) { - if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { - Ok((None, Api::OpenGl)) - } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { - Ok((None, Api::OpenGlEs)) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else { - Ok((None, Api::OpenGlEs)) - } - } - GlRequest::Specific(Api::OpenGlEs, version) => { - if egl_version >= (1, 2) && egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); - } - Ok((Some(version), Api::OpenGlEs)) - } - GlRequest::Specific(Api::OpenGl, version) => { - if egl_version < (1, 4) { - return Err(CreationError::OpenGlVersionNotSupported); - } - if egl.BindAPI(ffi::egl::OPENGL_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); - } - Ok((Some(version), Api::OpenGl)) - } - GlRequest::Specific(_, _) => Err(CreationError::OpenGlVersionNotSupported), - GlRequest::GlThenGles { opengles_version, opengl_version } => { - if egl_version >= (1, 4) { - if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { - Ok((Some(opengl_version), Api::OpenGl)) - } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { - Ok((Some(opengles_version), Api::OpenGlEs)) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else { - Ok((Some(opengles_version), Api::OpenGlEs)) - } - } - } -} - -fn get_native_display(native_display: &NativeDisplay) -> *const raw::c_void { +/// Obtain the error from the EGL. +fn check_error() -> Result<()> { let egl = EGL.as_ref().unwrap(); - // the first step is to query the list of extensions without any display, if - // supported - let dp_extensions = unsafe { - let p = egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); - - // this possibility is available only with EGL 1.5 or - // EGL_EXT_platform_base, otherwise `eglQueryString` returns an - // error - if p.is_null() { - vec![] - } else { - let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_default(); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - - let has_dp_extension = |e: &str| dp_extensions.iter().any(|s| s == e); - - match *native_display { - // Note: Some EGL implementations are missing the - // `eglGetPlatformDisplay(EXT)` symbol despite reporting - // `EGL_EXT_platform_base`. I'm pretty sure this is a bug. - // Therefore we detect whether the symbol is loaded in addition to - // checking for extensions. - NativeDisplay::X11(display) - if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - // TODO: `PLATFORM_X11_SCREEN_KHR` - unsafe { - egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::X11(display) - if has_dp_extension("EGL_EXT_platform_x11") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - // TODO: `PLATFORM_X11_SCREEN_EXT` - unsafe { - egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Gbm(display) - if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Gbm(display) - if has_dp_extension("EGL_MESA_platform_gbm") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null()) - } - } - - NativeDisplay::Wayland(display) - if has_dp_extension("EGL_KHR_platform_wayland") - && egl.GetPlatformDisplay.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_WAYLAND_KHR, - d as *mut _, - std::ptr::null(), - ) - } - } - - NativeDisplay::Wayland(display) - if has_dp_extension("EGL_EXT_platform_wayland") - && egl.GetPlatformDisplayEXT.is_loaded() => - { - let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { - egl.GetPlatformDisplayEXT( - ffi::egl::PLATFORM_WAYLAND_EXT, - d as *mut _, - std::ptr::null(), - ) - } - } - - NativeDisplay::Android - if has_dp_extension("EGL_KHR_platform_android") - && egl.GetPlatformDisplay.is_loaded() => - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_ANDROID_KHR, - ffi::egl::DEFAULT_DISPLAY as *mut _, - std::ptr::null(), - ) - }, - - NativeDisplay::Device(display) - if has_dp_extension("EGL_EXT_platform_device") - && egl.GetPlatformDisplay.is_loaded() => - unsafe { - egl.GetPlatformDisplay( - ffi::egl::PLATFORM_DEVICE_EXT, - display as *mut _, - std::ptr::null(), - ) - }, - - NativeDisplay::X11(Some(display)) - | NativeDisplay::Gbm(Some(display)) - | NativeDisplay::Wayland(Some(display)) - | NativeDisplay::Device(display) - | NativeDisplay::Other(Some(display)) => unsafe { egl.GetDisplay(display as *mut _) }, - - NativeDisplay::X11(None) - | NativeDisplay::Gbm(None) - | NativeDisplay::Wayland(None) - | NativeDisplay::Android - | NativeDisplay::Other(None) => unsafe { - egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) - }, - } -} - -#[allow(dead_code)] // Not all platforms use all -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum SurfaceType { - PBuffer, - Window, - Surfaceless, -} - -impl Context { - /// Start building an EGL context. - /// - /// This function initializes some things and chooses the pixel format. - /// - /// To finish the process, you must call [`ContextPrototype::finish()`]. - #[allow(clippy::new_ret_no_self)] - pub fn new<'a, F>( - pf_reqs: &PixelFormatRequirements, - opengl: &'a GlAttributes<&'a Context>, - native_display: NativeDisplay, - surface_type: SurfaceType, - config_selector: F, - ) -> Result, CreationError> - where - F: FnMut( - Vec, - ffi::egl::types::EGLDisplay, - ) -> Result, - { - let egl = EGL.as_ref().unwrap(); - // calling `eglGetDisplay` or equivalent - let display = get_native_display(&native_display); - - if display.is_null() { - return Err(CreationError::OsError("Could not create EGL display object".to_string())); - } - - let egl_version = get_egl_version(display)?; - - // the list of extensions supported by the client once initialized is - // different from the list of extensions obtained earlier - let extensions = if egl_version >= (1, 2) { - let p = - unsafe { CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) }; - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_default(); - list.split(' ').map(|e| e.to_string()).collect::>() - } else { - vec![] - }; - - // binding the right API and choosing the version - let (version, api) = unsafe { bind_and_get_api(opengl, egl_version)? }; - - let (config_id, pixel_format) = unsafe { - choose_fbconfig( - display, - &egl_version, - api, - version, - pf_reqs, - surface_type, - opengl, - config_selector, - )? - }; - - Ok(ContextPrototype { - opengl, - display, - egl_version, - extensions, - api, - version, - config_id, - pixel_format, - }) - } - - unsafe fn check_make_current(&self, ret: Option) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - if ret == Some(0) { - match egl.GetError() as u32 { - ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost), - err => { - panic!("make_current: eglMakeCurrent failed (eglGetError returned 0x{:x})", err) - } - } - } else { - Ok(()) - } - } - - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE); - let ret = egl.MakeCurrent(self.display, surface, surface, self.context); - - self.check_make_current(Some(ret)) - } - - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - - let surface_eq = if let Some(surface) = self.surface.as_ref().map(|s| *s.lock()) { - egl.GetCurrentSurface(ffi::egl::DRAW as i32) == surface - || egl.GetCurrentSurface(ffi::egl::READ as i32) == surface - } else { - false - }; - - if surface_eq || egl.GetCurrentContext() == self.context { - let ret = egl.MakeCurrent( - self.display, - ffi::egl::NO_SURFACE, - ffi::egl::NO_SURFACE, - ffi::egl::NO_CONTEXT, - ); - - self.check_make_current(Some(ret)) - } else { - self.check_make_current(None) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - let egl = EGL.as_ref().unwrap(); - unsafe { egl.GetCurrentContext() == self.context } - } - - #[inline] - pub fn get_api(&self) -> Api { - self.api - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::egl::types::EGLContext { - self.context - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> ffi::egl::types::EGLDisplay { - self.display - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let egl = EGL.as_ref().unwrap(); - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - unsafe { egl.GetProcAddress(addr) as *const _ } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().unwrap().lock(); - if *surface == ffi::egl::NO_SURFACE { - return Err(ContextError::ContextLost); - } - - let ret = unsafe { egl.SwapBuffers(self.display, *surface) }; - - if ret == 0 { - match unsafe { egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost), - err => { - panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err) - } - } - } else { - Ok(()) - } - } - - #[inline] - #[cfg(not(target_os = "windows"))] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - let egl = EGL.as_ref().unwrap(); - - if !egl.SwapBuffersWithDamageKHR.is_loaded() { - return Err(ContextError::FunctionUnavailable); - } - - let surface = self.surface.as_ref().unwrap().lock(); - if *surface == ffi::egl::NO_SURFACE { - return Err(ContextError::ContextLost); - } - - let mut ffirects: Vec = Vec::with_capacity(rects.len() * 4); - - for rect in rects { - ffirects.push(rect.x as ffi::egl::types::EGLint); - ffirects.push(rect.y as ffi::egl::types::EGLint); - ffirects.push(rect.width as ffi::egl::types::EGLint); - ffirects.push(rect.height as ffi::egl::types::EGLint); - } - - let ret = unsafe { - egl.SwapBuffersWithDamageKHR( - self.display, - *surface, - ffirects.as_mut_ptr(), - rects.len() as ffi::egl::types::EGLint, - ) - }; - - if ret == ffi::egl::FALSE { - match unsafe { egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost), - err => { - panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err) - } - } - } else { - Ok(()) - } - } - - #[inline] - #[cfg(not(target_os = "windows"))] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - let egl = EGL.as_ref().unwrap(); - egl.SwapBuffersWithDamageKHR.is_loaded() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().unwrap().lock(); - - let mut buffer_age = 0; - let result = unsafe { - egl.QuerySurface( - self.display, - *surface as *const _, - ffi::egl::BUFFER_AGE_EXT as i32, - &mut buffer_age, - ) - }; - - if result == ffi::egl::FALSE { - 0 - } else { - buffer_age as u32 - } - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Drop for Context { - fn drop(&mut self) { - unsafe { - // https://stackoverflow.com/questions/54402688/recreate-eglcreatewindowsurface-with-same-native-window - let egl = EGL.as_ref().unwrap(); - let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE); - // Ok, so we got to call `glFinish` before destroying the context - // to ensure it actually gets destroyed. This requires making the - // this context current. - let mut guard = MakeCurrentGuard::new(self.display, surface, surface, self.context) - .map_err(ContextError::OsError) - .unwrap(); - - guard.if_any_same_then_invalidate(surface, surface, self.context); - - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(!gl_finish_fn.is_null()); - let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn); - gl_finish_fn(); - - egl.DestroyContext(self.display, self.context); - self.context = ffi::egl::NO_CONTEXT; - egl.DestroySurface(self.display, surface); - if let Some(ref surface) = self.surface { - let mut surface = surface.lock(); - *surface = ffi::egl::NO_SURFACE; - } - - // In a reasonable world, we could uncomment the line bellow. - // - // This is no such world. Lets talk about something. - // - // You see, every call to `get_native_display` returns the exact - // same display, just look at the docs: - // - // "Multiple calls made to eglGetDisplay with the same display_id - // will return the same EGLDisplay handle." - // - // My EGL implementation does not do any ref counting, nor do the - // EGL docs mention ref counting anywhere. In fact, the docs state - // that there will be *no effect*, which, in a way, implies no ref - // counting: - // - // "Initializing an already initialized EGL display connection has - // no effect besides returning the version numbers." - // - // So, if we terminate the display, other people who are using it - // won't be so happy. - // - // Well, how did I stumble on this issue you might ask... - // - // In this case, the "other people" was us, for it appears my EGL - // implementation does not follow the docs, or maybe I'm misreading - // them. You see, according to the egl docs: - // - // "To release the current context without assigning a new one, set - // context to EGL_NO_CONTEXT and set draw and read to - // EGL_NO_SURFACE. [...] ******This is the only case where an - // uninitialized display may be passed to eglMakeCurrent.******" - // (Emphasis mine). - // - // Well, my computer returns EGL_BAD_DISPLAY if the display passed - // to eglMakeCurrent is uninitialized, which allowed to me to spot - // this issue. - // - // I would have assumed that if EGL was going to provide us with - // the same EGLDisplay that they'd at least do - // some ref counting, but they don't. - // - // FIXME: Technically we are leaking resources, not much we can do. - // Yeah, we could have a global static that does ref counting - // ourselves, but what if some other library is using the display. - // - // On unix operating systems, we could preload a little lib that - // does ref counting on that level, but: - // A) What about other platforms? - // B) Do you *really* want all glutin programs to preload a - // library? - // C) Who the hell is going to maintain that? - // - // egl.Terminate(self.display); - } - } -} - -#[derive(Debug)] -pub struct ContextPrototype<'a> { - opengl: &'a GlAttributes<&'a Context>, - display: ffi::egl::types::EGLDisplay, - egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), - extensions: Vec, - api: Api, - version: Option<(u8, u8)>, - config_id: ffi::egl::types::EGLConfig, - pixel_format: PixelFormat, -} - -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#[cfg(feature = "x11")] -pub fn get_native_visual_id( - display: ffi::egl::types::EGLDisplay, - config_id: ffi::egl::types::EGLConfig, -) -> ffi::egl::types::EGLint { - let egl = EGL.as_ref().unwrap(); - let mut value = unsafe { std::mem::zeroed() }; - let ret = unsafe { - egl.GetConfigAttrib( - display, - config_id, - ffi::egl::NATIVE_VISUAL_ID as ffi::egl::types::EGLint, - &mut value, - ) - }; - if ret == 0 { - panic!("get_native_visual_id: eglGetConfigAttrib failed with 0x{:x}", unsafe { - egl.GetError() - }) - }; - value -} - -impl<'a> ContextPrototype<'a> { - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - #[cfg(feature = "x11")] - pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint { - get_native_visual_id(self.display, self.config_id) - } - - pub fn finish(self, nwin: ffi::EGLNativeWindowType) -> Result { - let egl = EGL.as_ref().unwrap(); - let surface = unsafe { - let surface = - egl.CreateWindowSurface(self.display, self.config_id, nwin, std::ptr::null()); - if surface.is_null() { - return Err(CreationError::OsError("eglCreateWindowSurface failed".to_string())); - } - surface - }; - - self.finish_impl(Some(surface)) - } - - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - pub fn finish_surfaceless(self) -> Result { - // FIXME: Also check for the GL_OES_surfaceless_context *CONTEXT* - // extension - if !self.extensions.iter().any(|s| s == "EGL_KHR_surfaceless_context") { - Err(CreationError::NotSupported("EGL surfaceless not supported".to_string())) - } else { - self.finish_impl(None) - } - } - - #[cfg(any( - target_os = "android", - target_os = "windows", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - pub fn finish_pbuffer(self, size: dpi::PhysicalSize) -> Result { - let size: (u32, u32) = size.into(); - - let egl = EGL.as_ref().unwrap(); - let attrs = &[ - ffi::egl::WIDTH as raw::c_int, - size.0 as raw::c_int, - ffi::egl::HEIGHT as raw::c_int, - size.1 as raw::c_int, - ffi::egl::NONE as raw::c_int, - ]; - - let surface = unsafe { - let surface = egl.CreatePbufferSurface(self.display, self.config_id, attrs.as_ptr()); - if surface.is_null() || surface == ffi::egl::NO_SURFACE { - return Err(CreationError::OsError("eglCreatePbufferSurface failed".to_string())); - } - surface - }; - - self.finish_impl(Some(surface)) - } - - fn finish_impl( - self, - surface: Option, - ) -> Result { - let share = match self.opengl.sharing { - Some(ctx) => ctx.context, - None => std::ptr::null(), - }; - - let context = unsafe { - if let Some(version) = self.version { - create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - version, - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - )? - } else if self.api == Api::OpenGlEs { - if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (2, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (1, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (3, 2), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (3, 1), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else if let Ok(ctx) = create_context( - self.display, - &self.egl_version, - &self.extensions, - self.api, - (1, 0), - self.config_id, - self.opengl.debug, - self.opengl.robustness, - share, - ) { - ctx - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - }; - - if let Some(surface) = surface { - // VSync defaults to enabled; disable it if it was not requested. - if !self.opengl.vsync { - let _guard = MakeCurrentGuard::new(self.display, surface, surface, context) - .map_err(CreationError::OsError)?; - - let egl = EGL.as_ref().unwrap(); - unsafe { - if egl.SwapInterval(self.display, 0) == ffi::egl::FALSE { - panic!("finish_impl: eglSwapInterval failed: 0x{:x}", egl.GetError()); - } - } - } - } - - Ok(Context { - display: self.display, - context, - surface: surface.map(parking_lot::Mutex::new), - api: self.api, - pixel_format: self.pixel_format, - }) - } -} - -unsafe fn choose_fbconfig( - display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - api: Api, - version: Option<(u8, u8)>, - pf_reqs: &PixelFormatRequirements, - surface_type: SurfaceType, - opengl: &GlAttributes<&Context>, - mut config_selector: F, -) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> -where - F: FnMut( - Vec, - ffi::egl::types::EGLDisplay, - ) -> Result, -{ - let egl = EGL.as_ref().unwrap(); - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - if egl_version >= &(1, 2) { - out.push(ffi::egl::COLOR_BUFFER_TYPE as raw::c_int); - out.push(ffi::egl::RGB_BUFFER as raw::c_int); - } - - out.push(ffi::egl::SURFACE_TYPE as raw::c_int); - let surface_type = match surface_type { - SurfaceType::Window => ffi::egl::WINDOW_BIT, - SurfaceType::PBuffer => ffi::egl::PBUFFER_BIT, - SurfaceType::Surfaceless => 0, - }; - out.push(surface_type as raw::c_int); - - match (api, version) { - (Api::OpenGlEs, Some((3, _))) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int); - } - (Api::OpenGlEs, Some((2, _))) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int); - } - (Api::OpenGlEs, _) => { - if egl_version >= &(1, 3) { - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int); - } - } - (Api::OpenGl, _) => { - if egl_version < &(1, 3) { - return Err(CreationError::NoAvailablePixelFormat); - } - out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int); - out.push(ffi::egl::OPENGL_BIT as raw::c_int); - out.push(ffi::egl::CONFORMANT as raw::c_int); - out.push(ffi::egl::OPENGL_BIT as raw::c_int); - } - (_, _) => unimplemented!(), + unsafe { + let raw_code = egl.GetError() as egl::types::EGLenum; + let kind = match raw_code { + egl::SUCCESS => return Ok(()), + egl::NOT_INITIALIZED => ErrorKind::InitializationFailed, + egl::BAD_ACCESS => ErrorKind::BadAccess, + egl::BAD_ALLOC => ErrorKind::OutOfMemory, + egl::BAD_ATTRIBUTE => ErrorKind::BadAttribute, + egl::BAD_CONTEXT => ErrorKind::BadContext, + egl::BAD_CONFIG => ErrorKind::BadConfig, + egl::BAD_CURRENT_SURFACE => ErrorKind::BadCurrentSurface, + egl::BAD_DISPLAY => ErrorKind::BadDisplay, + egl::BAD_SURFACE => ErrorKind::BadSurface, + egl::BAD_MATCH => ErrorKind::BadMatch, + egl::BAD_PARAMETER => ErrorKind::BadParameter, + egl::BAD_NATIVE_PIXMAP => ErrorKind::BadNativePixmap, + egl::BAD_NATIVE_WINDOW => ErrorKind::BadNativeWindow, + egl::CONTEXT_LOST => ErrorKind::ContextLost, + _ => ErrorKind::Misc, }; - if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated { - out.push(ffi::egl::CONFIG_CAVEAT as raw::c_int); - out.push(if hardware_accelerated { - ffi::egl::NONE as raw::c_int - } else { - ffi::egl::SLOW_CONFIG as raw::c_int - }); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(ffi::egl::RED_SIZE as raw::c_int); - out.push((color / 3) as raw::c_int); - out.push(ffi::egl::GREEN_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int); - out.push(ffi::egl::BLUE_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(ffi::egl::ALPHA_SIZE as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(ffi::egl::DEPTH_SIZE as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(ffi::egl::STENCIL_SIZE as raw::c_int); - out.push(stencil as raw::c_int); - } - - if let Some(true) = pf_reqs.double_buffer { - return Err(CreationError::NoAvailablePixelFormat); - } - - if let Some(multisampling) = pf_reqs.multisampling { - out.push(ffi::egl::SAMPLES as raw::c_int); - out.push(multisampling as raw::c_int); - } - - if pf_reqs.stereoscopy { - return Err(CreationError::NoAvailablePixelFormat); - } - - if let Some(xid) = pf_reqs.x11_visual_xid { - out.push(ffi::egl::NATIVE_VISUAL_ID as raw::c_int); - out.push(xid as raw::c_int); - } - - // FIXME: srgb is not taken into account - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - // TODO: with EGL you need to manually set the behavior - unimplemented!() - } - } - - out.push(ffi::egl::NONE as raw::c_int); - out - }; - - // calling `eglChooseConfig` - let mut num_configs = std::mem::zeroed(); - if egl.ChooseConfig(display, descriptor.as_ptr(), std::ptr::null_mut(), 0, &mut num_configs) - == 0 - { - return Err(CreationError::OsError("eglChooseConfig failed".to_string())); + Err(Error::new(Some(raw_code as i64), None, kind)) } - - if num_configs == 0 { - return Err(CreationError::NoAvailablePixelFormat); - } - - let mut config_ids = Vec::with_capacity(num_configs as usize); - config_ids.resize_with(num_configs as usize, || std::mem::zeroed()); - if egl.ChooseConfig( - display, - descriptor.as_ptr(), - config_ids.as_mut_ptr(), - num_configs, - &mut num_configs, - ) == 0 - { - return Err(CreationError::OsError("eglChooseConfig failed".to_string())); - } - - // We're interested in those configs which allow our desired VSync. - let desired_swap_interval = if opengl.vsync { 1 } else { 0 }; - - let config_ids = config_ids - .into_iter() - .filter(|&config| { - let mut min_swap_interval = 0; - let _res = egl.GetConfigAttrib( - display, - config, - ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint, - &mut min_swap_interval, - ); - - if desired_swap_interval < min_swap_interval { - return false; - } - - let mut max_swap_interval = 0; - let _res = egl.GetConfigAttrib( - display, - config, - ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint, - &mut max_swap_interval, - ); - - if desired_swap_interval > max_swap_interval { - return false; - } - - true - }) - .collect::>(); - - if config_ids.is_empty() { - return Err(CreationError::NoAvailablePixelFormat); - } - - let config_id = - config_selector(config_ids, display).map_err(|_| CreationError::NoAvailablePixelFormat)?; - - // analyzing each config - macro_rules! attrib { - ($egl:expr, $display:expr, $config:expr, $attr:expr) => {{ - let mut value = std::mem::zeroed(); - let res = $egl.GetConfigAttrib( - $display, - $config, - $attr as ffi::egl::types::EGLint, - &mut value, - ); - if res == 0 { - return Err(CreationError::OsError("eglGetConfigAttrib failed".to_string())); - } - value - }}; - } - - let desc = PixelFormat { - hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, - color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 - + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 - + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8, - alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8, - depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8, - stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8, - stereoscopy: false, - double_buffer: true, - multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) { - 0 | 1 => None, - a => Some(a as u16), - }, - srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that - }; - - Ok((config_id, desc)) -} - -unsafe fn create_context( - display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - extensions: &[String], - api: Api, - version: (u8, u8), - config_id: ffi::egl::types::EGLConfig, - gl_debug: bool, - gl_robustness: Robustness, - share: ffi::EGLContext, -) -> Result { - let egl = EGL.as_ref().unwrap(); - - let mut context_attributes = Vec::with_capacity(10); - let mut flags = 0; - - if egl_version >= &(1, 5) || extensions.iter().any(|s| s == "EGL_KHR_create_context") { - context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); - context_attributes.push(version.0 as i32); - context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); - context_attributes.push(version.1 as i32); - - // handling robustness - let supports_robustness = egl_version >= &(1, 5) - || extensions.iter().any(|s| s == "EGL_EXT_create_context_robustness"); - - match gl_robustness { - Robustness::NotRobust => (), - - Robustness::NoError => { - if extensions.iter().any(|s| s == "EGL_KHR_create_context_no_error") { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as raw::c_int); - context_attributes.push(1); - } - } - - Robustness::RobustNoResetNotification => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int); - flags |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } else { - return Err(CreationError::RobustnessNotSupported); - } - } - - Robustness::TryRobustNoResetNotification => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int); - flags |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } - } - - Robustness::RobustLoseContextOnReset => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int); - flags |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } else { - return Err(CreationError::RobustnessNotSupported); - } - } - - Robustness::TryRobustLoseContextOnReset => { - if supports_robustness { - context_attributes - .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int); - flags |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int; - } - } - } - - if gl_debug && egl_version >= &(1, 5) { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); - context_attributes.push(ffi::egl::TRUE as i32); - - // TODO: using this flag sometimes generates an error - // there was a change in the specs that added this flag, so it - // may not be supported everywhere ; however it is - // not possible to know whether it is supported or - // not flags = flags | - // ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; - } - - // In at least some configurations, the Android emulator’s GL - // implementation advertises support for the - // EGL_KHR_create_context extension but returns BAD_ATTRIBUTE - // when CONTEXT_FLAGS_KHR is used. - if flags != 0 { - context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); - context_attributes.push(flags); - } - } else if egl_version >= &(1, 3) && api == Api::OpenGlEs { - // robustness is not supported - match gl_robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - - context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); - context_attributes.push(version.0 as i32); - } - - context_attributes.push(ffi::egl::NONE as i32); - - let context = egl.CreateContext(display, config_id, share, context_attributes.as_ptr()); - - if context.is_null() { - match egl.GetError() as u32 { - ffi::egl::BAD_MATCH | ffi::egl::BAD_ATTRIBUTE => { - return Err(CreationError::OpenGlVersionNotSupported); - } - e => panic!("create_context: eglCreateContext failed: 0x{:x}", e), - } - } - - Ok(context) } diff --git a/glutin/src/api/egl/surface.rs b/glutin/src/api/egl/surface.rs new file mode 100644 index 0000000000..4a05ae1655 --- /dev/null +++ b/glutin/src/api/egl/surface.rs @@ -0,0 +1,512 @@ +//! 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::config::GetGlConfig; +use crate::context::Version; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{ + AsRawSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, 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); + + // Add information about render buffer. + attrs.push(egl::RENDER_BUFFER as EGLint); + let buffer = + if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER } + as EGLint; + attrs.push(buffer); + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLint); + + let config = config.clone(); + let surface = 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() + && self.inner.client_extensions.contains("EGL_KHR_gl_colorspace") + { + attrs.push(egl::GL_COLORSPACE as EGLAttrib); + let colorspace = match surface_attributes.srgb { + Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, + _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, + }; + attrs.push(colorspace); + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + let config = config.clone(); + let surface = if self.inner.version >= Version::new(1, 5) + && self.inner.egl.CreatePlatformWindowSurface.is_loaded() + { + self.inner.egl.CreatePlatformPixmapSurface( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ) + } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePlatformPixmapSurfaceEXT( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ) + } else { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePixmapSurface( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + 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() + && self.inner.client_extensions.contains("EGL_KHR_gl_colorspace") + { + attrs.push(egl::GL_COLORSPACE as EGLAttrib); + let colorspace = match surface_attributes.srgb { + Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, + _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, + }; + attrs.push(colorspace); + } + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLAttrib); + + let config = config.clone(); + + let surface = if self.inner.version >= Version::new(1, 5) + && self.inner.egl.CreatePlatformWindowSurface.is_loaded() + { + self.inner.egl.CreatePlatformWindowSurface( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + attrs.as_ptr(), + ) + } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePlatformWindowSurfaceEXT( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + attrs.as_ptr(), + ) + } else { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreateWindowSurface( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + 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 Surface { + /// Swaps the underlying back buffers when the surface is not single + /// buffered and pass the [`DamageRect`] information to the system + /// compositor. Providing empty slice will result in damaging the entire + /// surface. + /// + /// This Api doesn't do any partial rendering, it just sends the provides + /// the hints for the system compositor. + pub fn swap_buffers_with_damage( + &self, + _context: &PossiblyCurrentContext, + rects: &[DamageRect], + ) -> Result<()> { + unsafe { + if self.display.inner.egl.SwapBuffersWithDamageKHR( + *self.display.inner.raw, + self.raw, + rects.as_ptr() as *mut _, + (rects.len() * 4) as _, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + + fn raw_attribute(&self, attr: EGLint) -> EGLint { + unsafe { + let mut value = 0; + self.display.inner.egl.QuerySurface( + *self.display.inner.raw, + self.raw, + attr, + &mut value, + ); + value + } + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + self.display.inner.egl.DestroySurface(*self.display.inner.raw, self.raw); + } + } +} + +impl GlSurface for Surface { + type Context = PossiblyCurrentContext; + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) as u32 + } + + fn width(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + fn height(&self) -> Option { + Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + } + + fn is_single_buffered(&self) -> bool { + self.raw_attribute(egl::RENDER_BUFFER as EGLint) != egl::SINGLE_BUFFER as i32 + } + + fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { + unsafe { + 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 { + 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 { self.display.inner.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.raw } + } + + fn is_current_read(&self, _context: &Self::Context) -> bool { + unsafe { 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 { + #[cfg(wayland_platform)] + Wayland(*mut ffi::c_void), + + #[cfg(x11_platform)] + Xlib(std::os::raw::c_ulong), + + #[cfg(x11_platform)] + Xcb(u32), + + Android(*mut ffi::c_void), + + Win32(*mut ffi::c_void), + + 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.cast(), + _width.get() as _, + _height.get() as _ + ); + if ptr.is_null() { + return Err(ErrorKind::OutOfMemory.into()); + } + Self::Wayland(ptr.cast()) + }, + #[cfg(x11_platform)] + RawWindowHandle::Xlib(window_handle) => Self::Xlib(window_handle.window as _), + #[cfg(x11_platform)] + RawWindowHandle::Xcb(window_handle) => Self::Xcb(window_handle.window as _), + RawWindowHandle::AndroidNdk(window_handle) => { + Self::Android(window_handle.a_native_window) + }, + RawWindowHandle::Win32(window_hanlde) => Self::Win32(window_hanlde.hwnd), + RawWindowHandle::Gbm(window_handle) => Self::Gbm(window_handle.gbm_surface), + _ => { + return Err( + ErrorKind::NotSupported("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 + ) + } + } + } + + fn raw(&self) -> *mut ffi::c_void { + match self { + #[cfg(wayland_platform)] + Self::Wayland(wl_egl_surface) => *wl_egl_surface, + #[cfg(x11_platform)] + Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void, + #[cfg(x11_platform)] + Self::Xcb(window_id) => window_id as *const _ as *mut ffi::c_void, + Self::Win32(hwnd) => *hwnd, + Self::Android(a_native_window) => *a_native_window, + Self::Gbm(gbm_surface) => *gbm_surface, + } + } +} + +#[cfg(wayland_platform)] +impl Drop for NativeWindow { + fn drop(&mut self) { + unsafe { + if let Self::Wayland(wl_egl_window) = self { + ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, wl_egl_window.cast()); + } + } + } +} + +impl NativePixmap { + fn raw(&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 _ as *mut _, + } + } +} + +/// The damage rect that is being used in [`Surface::swap_buffers_with_damage`]. +/// The origin is in the bottom left of the surface. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +pub struct DamageRect { + /// `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 DamageRect { + /// Helper to simplify damage rectangle creation. + pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self { + Self { x, y, width, height } + } +} diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs new file mode 100644 index 0000000000..7c659e9b34 --- /dev/null +++ b/glutin/src/api/glx/config.rs @@ -0,0 +1,346 @@ +//! 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::context::Version; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB}; +use crate::private::Sealed; + +use super::display::Display; + +const FLOAT_PIXEL_EXT: &str = "GLX_ARB_fbconfig_float"; + +impl Display { + pub(crate) 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.client_extensions.contains(FLOAT_PIXEL_EXT) { + 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); + } + + // 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 { + 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 supported. + if self.inner.version >= Version::new(1, 4) + || self.inner.client_extensions.contains("GLX_ARB_multisample") + { + config_attributes.push(glx::SAMPLE_BUFFERS as c_int); + config_attributes.push(template.sample_buffers as c_int); + } + + // Push `glx::NONE` to terminate the list. + config_attributes.push(glx::NONE as c_int); + + unsafe { + let mut num_configs = 0; + let raw_configs = self.inner.glx.ChooseFBConfig( + self.inner.raw.cast(), + self.inner.screen as _, + config_attributes.as_ptr() as *const _, + &mut num_configs, + ); + + if raw_configs.is_null() { + return 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| { + if template.transparency { + config.x11_visual().unwrap().supports_transparency() + } else { + true + } + }); + + Ok(Box::new(iter)) + } + } +} + +/// A wrapper around `GLXFBConfig`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + pub(crate) inner: Arc, +} + +impl Config { + fn raw_attribute(&self, attr: c_int) -> c_int { + unsafe { + let mut val = 0; + self.inner.display.inner.glx.GetFBConfigAttrib( + self.inner.display.inner.raw.cast(), + *self.inner.raw, + attr, + &mut val, + ); + val as c_int + } + } + + pub(crate) fn is_singe_buffered(&self) -> bool { + self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 + } +} + +impl GlConfig for Config { + fn color_buffer_type(&self) -> ColorBufferType { + match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ { + glx::TRUE_COLOR => { + let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8; + let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8; + let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8; + ColorBufferType::Rgb { r_size, g_size, b_size } + }, + glx::GRAY_SCALE => { + let luma = self.raw_attribute(glx::RED_SIZE as c_int); + ColorBufferType::Luminance(luma as u8) + }, + _ => unimplemented!(), + } + } + + fn float_pixels(&self) -> bool { + if self.inner.display.inner.client_extensions.contains(FLOAT_PIXEL_EXT) { + let render_type = self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum; + render_type == glx_extra::RGBA_FLOAT_BIT_ARB + } else { + false + } + } + + fn alpha_size(&self) -> u8 { + self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 + } + + fn srgb_capable(&self) -> bool { + if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 + } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { + self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + } else { + false + } + } + + fn depth_size(&self) -> u8 { + self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 + } + + fn stencil_size(&self) -> u8 { + self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 + } + + fn sample_buffers(&self) -> u8 { + self.raw_attribute(glx::SAMPLE_BUFFERS as c_int) as u8 + } + + fn config_surface_types(&self) -> ConfigSurfaceTypes { + let mut ty = ConfigSurfaceTypes::empty(); + + let raw_ty = self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32; + if raw_ty & glx::WINDOW_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::WINDOW); + } + if raw_ty & glx::PBUFFER_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PBUFFER); + } + if raw_ty & glx::PIXMAP_BIT as u32 != 0 { + ty.insert(ConfigSurfaceTypes::PIXMAP); + } + + ty + } + + fn api(&self) -> Api { + Api::OPENGL + } +} + +impl X11GlConfigExt for Config { + fn x11_visual(&self) -> Option { + unsafe { + let raw_visual = self + .inner + .display + .inner + .glx + .GetVisualFromFBConfig(self.inner.display.inner.raw.cast(), *self.inner.raw); + if raw_visual.is_null() { + None + } else { + Some(X11VisualInfo::from_raw( + self.inner.display.inner.raw.cast(), + raw_visual as *mut _, + )) + } + } + } +} + +impl GetGlDisplay for Config { + type Target = Display; + + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl 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 + } +} diff --git a/glutin/src/api/glx/context.rs b/glutin/src/api/glx/context.rs new file mode 100644 index 0000000000..4633cc69bf --- /dev/null +++ b/glutin/src/api/glx/context.rs @@ -0,0 +1,420 @@ +//! Everything related to `GLXContext`. + +use std::ffi::{self, CStr}; +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::{ + AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, ReleaseBehaviour, + Robustness, +}; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let 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 = 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) + }; + + // Check the error from the GLX. + super::last_glx_error(self.inner.raw)?; + + // Failed to create the context. + if context.is_null() { + return Err(ErrorKind::BadContext.into()); + } + + let config = config.clone(); + let inner = ContextInner { display: self.clone(), config, raw: GlxContext(context) }; + + Ok(NotCurrentContext::new(inner)) + } + + fn create_context_arb( + &self, + config: &Config, + context_attributes: &ContextAttributes, + shared_context: GLXContext, + ) -> Result { + 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.client_extensions.contains("GLX_EXT_create_context_es2_profile") + || self.inner.client_extensions.contains("GLX_EXT_create_context_es_profile"); + + let (profile, version) = match context_attributes.api { + ContextApi::OpenGl(version) => { + let profile = context_attributes.profile.map(|profile| match profile { + GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + }); + (profile, version) + }, + ContextApi::Gles(version) if supports_es => { + (Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT), version) + }, + _ => { + 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; + if self.inner.client_extensions.contains("GLX_ARB_create_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.client_extensions.contains("GLX_ARB_create_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); + }, + } + } 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 { + 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.client_extensions.contains("GLX_ARB_context_flush_control") { + match context_attributes.release_behavior { + ReleaseBehaviour::Flush => { + attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); + attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB as c_int); + }, + ReleaseBehaviour::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 != ReleaseBehaviour::Flush { + return Err(ErrorKind::NotSupported( + "flush control behavior GLX_ARB_context_flush_control", + ) + .into()); + } + + // Terminate list with zero. + attrs.push(0); + + unsafe { + Ok(extra.CreateContextAttribsARB( + self.inner.raw.cast(), + *config.inner.raw, + shared_context, + // Direct context + 1, + attrs.as_ptr(), + )) + } + } + + fn create_context_legacy(&self, config: &Config, shared_context: GLXContext) -> GLXContext { + let render_type = + if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE }; + + 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 { + fn new(inner: ContextInner) -> Self { + Self { inner } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; + + 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 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 PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(self) -> Result { + self.inner.make_not_current()?; + Ok(NotCurrentContext::new(self.inner)) + } + + fn is_current(&self) -> bool { + unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw } + } + + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + unsafe { + self.inner.display.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ + } + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current_draw_read(surface, surface) + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + self.inner.make_current_draw_read(surface_draw, surface_read) + } +} + +impl 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, +} + +impl ContextInner { + fn make_current_draw_read( + &self, + surface_draw: &Surface, + surface_read: &Surface, + ) -> Result<()> { + unsafe { + if self.display.inner.glx.MakeContextCurrent( + self.display.inner.raw.cast(), + surface_draw.raw, + surface_read.raw, + *self.raw, + ) == 0 + { + super::last_glx_error(self.display.inner.raw) + } else { + Ok(()) + } + } + } + + fn make_not_current(&self) -> Result<()> { + unsafe { + if self.display.inner.glx.MakeContextCurrent( + self.display.inner.raw.cast(), + 0, + 0, + std::ptr::null(), + ) == 0 + { + super::last_glx_error(self.display.inner.raw) + } else { + Ok(()) + } + } + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + 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 + } +} diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs new file mode 100644 index 0000000000..cc5bbecdcc --- /dev/null +++ b/glutin/src/api/glx/display.rs @@ -0,0 +1,210 @@ +//! GLX object creation. + +use std::collections::HashSet; +use std::ffi::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, 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. + pub unsafe fn from_raw( + display: RawDisplayHandle, + error_hook_registrar: XlibErrorHookRegistrar, + ) -> Result { + // Don't load GLX when unsupported platform was requested. + let (display, screen) = match display { + RawDisplayHandle::Xlib(handle) => { + (GlxDisplay(handle.display as *mut _), handle.screen as i32) + }, + _ => { + return Err( + ErrorKind::NotSupported("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. + let mut error_base = 0; + let mut event_base = 0; + if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { + // The glx extension isn't present. + return Err(ErrorKind::InitializationFailed.into()); + } + GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); + + // This is completely ridiculous, but VirtualBox's OpenGL driver needs + // some call handled by *it* (i.e. not Mesa) to occur before + // anything else can happen. That is because VirtualBox's OpenGL + // driver is going to apply binary patches to Mesa in the DLL + // constructor and until it's loaded it won't have a chance to do that. + // + // The easiest way to do this is to just call `glXQueryVersion()` before + // doing anything else. See: https://www.virtualbox.org/ticket/8293 + let (mut major, mut minor) = (0, 0); + if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { + return Err(ErrorKind::InitializationFailed.into()); + } + + let version = 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 inner = Arc::new(DisplayInner { + raw: display, + glx, + glx_extra: GLX_EXTRA.as_ref(), + version, + screen, + client_extensions, + }); + + 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 + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + unsafe fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + unsafe fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Glx(self.inner.raw.cast()) + } +} + +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, + /// 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("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() + } + } +} diff --git a/glutin/src/api/glx/make_current_guard.rs b/glutin/src/api/glx/make_current_guard.rs deleted file mode 100644 index 5885a49d2a..0000000000 --- a/glutin/src/api/glx/make_current_guard.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::platform::unix::x11::XConnection; -use glutin_glx_sys as ffi; - -use std::sync::Arc; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct MakeCurrentGuard { - old_display: *mut ffi::Display, - display: *mut ffi::Display, - xconn: Arc, - possibly_invalid: Option, -} - -#[derive(Debug)] -struct MakeCurrentGuardInner { - old_drawable: ffi::glx::types::GLXDrawable, - old_context: ffi::GLXContext, -} - -impl MakeCurrentGuard { - pub fn new( - xconn: &Arc, - drawable: ffi::glx::types::GLXDrawable, - context: ffi::GLXContext, - ) -> Result { - unsafe { - let glx = super::GLX.as_ref().unwrap(); - - let ret = MakeCurrentGuard { - old_display: glx.GetCurrentDisplay() as *mut _, - display: xconn.display as *mut _, - xconn: Arc::clone(xconn), - possibly_invalid: Some(MakeCurrentGuardInner { - old_drawable: glx.GetCurrentDrawable(), - old_context: glx.GetCurrentContext(), - }), - }; - - let res = glx.MakeCurrent(xconn.display as *mut _, drawable, context); - - if res == 0 { - let err = xconn.check_errors(); - Err(format!("`glXMakeCurrent` failed: {:?}", err)) - } else { - Ok(ret) - } - } - } - - pub fn old_context(&mut self) -> Option { - self.possibly_invalid.as_ref().map(|pi| pi.old_context) - } - - pub fn invalidate(&mut self) { - self.possibly_invalid.take(); - } -} - -impl Drop for MakeCurrentGuard { - fn drop(&mut self) { - let glx = super::GLX.as_ref().unwrap(); - let (drawable, context) = match self.possibly_invalid.take() { - Some(inner) => (inner.old_drawable, inner.old_context), - None => (0, std::ptr::null()), - }; - - let display = if self.old_display.is_null() { self.display } else { self.old_display }; - - let res = unsafe { glx.MakeCurrent(display as *mut _, drawable, context) }; - - if res == 0 { - let err = self.xconn.check_errors(); - panic!("`glXMakeCurrent` failed: {:?}", err); - } - } -} diff --git a/glutin/src/api/glx/mod.rs b/glutin/src/api/glx/mod.rs index 08ef63c9de..dfd19c576f 100644 --- a/glutin/src/api/glx/mod.rs +++ b/glutin/src/api/glx/mod.rs @@ -1,775 +1,175 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#![cfg(feature = "x11")] +//! GLX platform Api. -mod make_current_guard; - -use std::ffi::{CStr, CString}; +use std::ffi::{self, CStr, CString}; use std::ops::{Deref, DerefMut}; -use std::os::raw; -use std::sync::Arc; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Mutex; -use glutin_glx_sys as ffi; +use libloading::Library; use once_cell::sync::Lazy; -use winit::dpi; +use 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`]. +/// +/// [`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)); + +static GLX: Lazy> = Lazy::new(|| { + let paths = ["libGL.so.1", "libGL.so"]; + + SymWrapper::new(&paths).map(Glx).ok() +}); -use self::make_current_guard::MakeCurrentGuard; -use crate::api::dlloader::{SymTrait, SymWrapper}; -use crate::platform::unix::x11::XConnection; -use crate::platform_impl::x11_utils::SurfaceType; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; +static GLX_EXTRA: Lazy> = Lazy::new(|| { + let glx = GLX.as_ref()?; + Some(GlxExtra::new(glx)) +}); -#[derive(Clone)] -pub struct Glx(SymWrapper); +pub(crate) struct Glx(pub SymWrapper); -/// Because `*const raw::c_void` doesn't implement `Sync`. unsafe impl Sync for Glx {} +unsafe impl Send for Glx {} -impl SymTrait for ffi::glx::Glx { - fn load_with(lib: &libloading::Library) -> Self { - Self::load_with(|sym| unsafe { - lib.get(std::ffi::CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) +impl SymLoading for glx::Glx { + unsafe fn load_with(lib: &Library) -> Self { + Self::load_with(|sym| { + lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul()) .map(|sym| *sym) .unwrap_or(std::ptr::null_mut()) }) } } -impl Glx { - pub fn new() -> Result { - let paths = vec!["libGL.so.1", "libGL.so"]; - - SymWrapper::new(paths).map(Glx) - } -} - impl Deref for Glx { - type Target = ffi::glx::Glx; + type Target = glx::Glx; - fn deref(&self) -> &ffi::glx::Glx { + fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Glx { - fn deref_mut(&mut self) -> &mut ffi::glx::Glx { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -pub static GLX: Lazy> = Lazy::new(|| Glx::new().ok()); - -#[derive(Debug)] -pub struct Context { - xconn: Arc, - drawable: ffi::Window, - context: ffi::GLXContext, - pixel_format: PixelFormat, -} - -impl Context { - // transparent is [`None`] if window is raw. - #[allow(clippy::new_ret_no_self)] - pub fn new<'a>( - xconn: Arc, - pf_reqs: &PixelFormatRequirements, - opengl: &'a GlAttributes<&'a Context>, - screen_id: raw::c_int, - surface_type: SurfaceType, - transparent: Option, - ) -> Result, CreationError> { - let glx = GLX.as_ref().unwrap(); - // This is completely ridiculous, but VirtualBox's OpenGL driver needs - // some call handled by *it* (i.e. not Mesa) to occur before - // anything else can happen. That is because VirtualBox's OpenGL - // driver is going to apply binary patches to Mesa in the DLL - // constructor and until it's loaded it won't have a chance to do that. - // - // The easiest way to do this is to just call `glXQueryVersion()` before - // doing anything else. See: https://www.virtualbox.org/ticket/8293 - let (mut major, mut minor) = (0, 0); - unsafe { - glx.QueryVersion(xconn.display as *mut _, &mut major, &mut minor); - } - - // loading the list of extensions - let extensions = load_extensions(&xconn, screen_id)?; - - // finding the pixel format we want - let (fb_config, pixel_format, visual_infos) = unsafe { - choose_fbconfig(&extensions, &xconn, screen_id, pf_reqs, surface_type, transparent)? - }; - - Ok(ContextPrototype { - extensions, - xconn, - opengl, - fb_config, - visual_infos: unsafe { std::mem::transmute(visual_infos) }, - pixel_format, - }) - } - - unsafe fn check_make_current(&self, ret: Option) -> Result<(), ContextError> { - if ret == Some(0) { - let err = self.xconn.check_errors(); - Err(ContextError::OsError(format!("`glXMakeCurrent` failed: {:?}", err))) - } else { - Ok(()) - } - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - let res = glx.MakeCurrent(self.xconn.display as *mut _, self.drawable, self.context); - self.check_make_current(Some(res)) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - if self.drawable == glx.GetCurrentDrawable() || self.context == glx.GetCurrentContext() { - let res = glx.MakeCurrent(self.xconn.display as *mut _, 0, std::ptr::null()); - self.check_make_current(Some(res)) - } else { - self.check_make_current(None) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - let glx = GLX.as_ref().unwrap(); - unsafe { glx.GetCurrentContext() == self.context } - } - - #[inline] - pub fn get_api(&self) -> crate::Api { - crate::Api::OpenGl - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::GLXContext { - self.context - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let glx = GLX.as_ref().unwrap(); - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - unsafe { glx.GetProcAddress(addr as *const _) as *const _ } - } +pub(crate) struct GlxExtra(glx_extra::Glx); - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - let glx = GLX.as_ref().unwrap(); - unsafe { - glx.SwapBuffers(self.xconn.display as *mut _, self.drawable); - } - if let Err(err) = self.xconn.check_errors() { - Err(ContextError::OsError(format!("`glXSwapBuffers` failed: {:?}", err))) - } else { - Ok(()) - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - let glx = GLX.as_ref().unwrap(); - - let mut buffer_age = 0; - - unsafe { - glx.QueryDrawable( - self.xconn.display as *mut _, - self.drawable, - ffi::glx_extra::BACK_BUFFER_AGE_EXT as i32, - &mut buffer_age, - ); - } - - buffer_age - } +unsafe impl Sync for GlxExtra {} +unsafe impl Send for GlxExtra {} +impl GlxExtra { #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() + 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 _ } + })) } } -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Drop for Context { - fn drop(&mut self) { - let glx = GLX.as_ref().unwrap(); - unsafe { - // See `drop` for `crate::api::egl::Context` for rationale. - let mut guard = MakeCurrentGuard::new(&self.xconn, self.drawable, self.context) - .map_err(ContextError::OsError) - .unwrap(); - - let gl_finish_fn = self.get_proc_address("glFinish"); - assert!(!gl_finish_fn.is_null()); - let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn); - gl_finish_fn(); - - if guard.old_context() == Some(self.context) { - guard.invalidate() - } - std::mem::drop(guard); +impl Deref for GlxExtra { + type Target = glx_extra::Glx; - glx.DestroyContext(self.xconn.display as *mut _, self.context); - } + fn deref(&self) -> &Self::Target { + &self.0 } } -#[derive(Debug)] -pub struct ContextPrototype<'a> { - extensions: String, - xconn: Arc, - opengl: &'a GlAttributes<&'a Context>, - fb_config: ffi::glx::types::GLXFBConfig, - visual_infos: ffi::XVisualInfo, - pixel_format: PixelFormat, -} - -impl<'a> ContextPrototype<'a> { +impl DerefMut for GlxExtra { #[inline] - pub fn get_visual_infos(&self) -> &ffi::XVisualInfo { - &self.visual_infos - } - - // creating GL context - fn create_context(&self) -> Result<(ffi::glx_extra::Glx, ffi::GLXContext), CreationError> { - let glx = GLX.as_ref().unwrap(); - let share = match self.opengl.sharing { - Some(ctx) => ctx.context, - None => std::ptr::null(), - }; - - // loading the extra GLX functions - let extra_functions = ffi::glx_extra::Glx::load_with(|proc_name| { - let c_str = CString::new(proc_name).unwrap(); - unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } - }); - - let context = match self.opengl.version { - GlRequest::Latest => { - let opengl_versions = [ - (4, 6), - (4, 5), - (4, 4), - (4, 3), - (4, 2), - (4, 1), - (4, 0), - (3, 3), - (3, 2), - (3, 1), - ]; - // Try all OpenGL versions in descending order because some - // non-compliant drivers don't return - // the latest supported version but the one requested - opengl_versions - .iter() - .find_map(|opengl_version| { - create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - *opengl_version, - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - ) - .ok() - }) - .map_or_else( - || { - create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (1, 0), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - ) - }, - Ok, - )? - } - GlRequest::Specific(Api::OpenGl, (major, minor)) => create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (major, minor), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - )?, - GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => create_context( - &extra_functions, - &self.extensions, - &self.xconn.xlib, - (major, minor), - self.opengl.profile, - self.opengl.debug, - self.opengl.robustness, - share, - self.xconn.display, - self.fb_config, - &self.visual_infos, - )?, - }; - - Ok((extra_functions, context)) - } - - pub fn finish_pbuffer(self, size: dpi::PhysicalSize) -> Result { - let glx = GLX.as_ref().unwrap(); - let size: (u32, u32) = size.into(); - let (_extra_functions, context) = self.create_context()?; - - let attributes: Vec = vec![ - ffi::glx::PBUFFER_WIDTH as raw::c_int, - size.0 as raw::c_int, - ffi::glx::PBUFFER_HEIGHT as raw::c_int, - size.1 as raw::c_int, - 0, - ]; - - let pbuffer = unsafe { - glx.CreatePbuffer(self.xconn.display as *mut _, self.fb_config, attributes.as_ptr()) - }; - - Ok(Context { - xconn: self.xconn, - drawable: pbuffer, - context, - pixel_format: self.pixel_format, - }) - } - - pub fn finish(self, window: ffi::Window) -> Result { - let glx = GLX.as_ref().unwrap(); - let (extra_functions, context) = self.create_context()?; - - // vsync - let swap_mode = if self.opengl.vsync { 1 } else { 0 }; - - let _guard = - MakeCurrentGuard::new(&self.xconn, window, context).map_err(CreationError::OsError)?; - - if check_ext(&self.extensions, "GLX_EXT_swap_control") - && extra_functions.SwapIntervalEXT.is_loaded() - { - // this should be the most common extension - unsafe { - extra_functions.SwapIntervalEXT(self.xconn.display as *mut _, window, swap_mode); - } - - let mut swap = unsafe { std::mem::zeroed() }; - unsafe { - glx.QueryDrawable( - self.xconn.display as *mut _, - window, - ffi::glx_extra::SWAP_INTERVAL_EXT as i32, - &mut swap, - ); - } - - if swap != swap_mode as u32 { - return Err(CreationError::OsError(format!( - "Couldn't setup vsync: expected interval `{}` but got `{}`", - swap_mode, swap - ))); - } - } else if check_ext(&self.extensions, "GLX_MESA_swap_control") - && extra_functions.SwapIntervalMESA.is_loaded() - { - unsafe { - extra_functions.SwapIntervalMESA(swap_mode as u32); - } - } else if check_ext(&self.extensions, "GLX_SGI_swap_control") - && extra_functions.SwapIntervalSGI.is_loaded() - { - unsafe { - extra_functions.SwapIntervalSGI(swap_mode); - } - } else if self.opengl.vsync { - return Err(CreationError::OsError( - "Couldn't find any available vsync extension".to_string(), - )); - } - - Ok(Context { - xconn: self.xconn, - drawable: window, - context, - pixel_format: self.pixel_format, - }) + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } - -extern "C" fn x_error_callback(_dpy: *mut ffi::Display, _err: *mut ffi::XErrorEvent) -> i32 { - 0 -} - -fn create_context( - extra_functions: &ffi::glx_extra::Glx, - extensions: &str, - xlib: &ffi::Xlib, - version: (u8, u8), - profile: Option, - debug: bool, - robustness: Robustness, - share: ffi::GLXContext, - display: *mut ffi::Display, - fb_config: ffi::glx::types::GLXFBConfig, - visual_infos: &ffi::XVisualInfo, -) -> Result { - let glx = GLX.as_ref().unwrap(); +/// Store the last error received from the GLX. +fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool { + let xerror = xerror_event as *mut XErrorEvent; unsafe { - let old_callback = (xlib.XSetErrorHandler)(Some(x_error_callback)); - let context = if check_ext(extensions, "GLX_ARB_create_context") { - let mut attributes = Vec::with_capacity(9); - - attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(version.0 as raw::c_int); - attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(version.1 as raw::c_int); - - if let Some(profile) = profile { - let flag = match profile { - GlProfile::Compatibility => { - ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB - } - GlProfile::Core => ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, - }; - - attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(flag as raw::c_int); - } - - let flags = { - let mut flags = 0; - - // robustness - if check_ext(extensions, "GLX_ARB_create_context_robustness") { - match robustness { - Robustness::RobustNoResetNotification - | Robustness::TryRobustNoResetNotification => { - attributes.push( - ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes - .push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as raw::c_int); - flags |= ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::RobustLoseContextOnReset - | Robustness::TryRobustLoseContextOnReset => { - attributes.push( - ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes - .push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as raw::c_int); - flags |= ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::NotRobust => (), - Robustness::NoError => (), - } - } else { - match robustness { - Robustness::RobustNoResetNotification - | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - } - - if debug { - flags |= ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int; - } - - flags - }; - - attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as raw::c_int); - attributes.push(flags); - - attributes.push(0); - - extra_functions.CreateContextAttribsARB( - display as *mut _, - fb_config, - share, - 1, - attributes.as_ptr(), - ) - } else { - let visual_infos: *const ffi::XVisualInfo = visual_infos; - glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1) - }; - - (xlib.XSetErrorHandler)(old_callback); - - if context.is_null() { - // TODO: check for errors and return `OpenGlVersionNotSupported` - return Err(CreationError::OsError("GL context creation failed".to_string())); - } - - Ok(context) - } -} - -/// Enumerates all available FBConfigs -unsafe fn choose_fbconfig( - extensions: &str, - xconn: &Arc, - screen_id: raw::c_int, - pf_reqs: &PixelFormatRequirements, - surface_type: SurfaceType, - transparent: Option, -) -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat, ffi::XVisualInfo), CreationError> { - let glx = GLX.as_ref().unwrap(); - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - out.push(ffi::glx::X_RENDERABLE as raw::c_int); - out.push(1); - - if let Some(xid) = pf_reqs.x11_visual_xid { - // getting the visual infos - let fvi = crate::platform_impl::x11_utils::get_visual_info_from_xid(xconn, xid); - - out.push(ffi::glx::X_VISUAL_TYPE as raw::c_int); - out.push(fvi.class as raw::c_int); - - out.push(ffi::glx::VISUAL_ID as raw::c_int); - out.push(xid as raw::c_int); - } else { - out.push(ffi::glx::X_VISUAL_TYPE as raw::c_int); - out.push(ffi::glx::TRUE_COLOR as raw::c_int); - } - - out.push(ffi::glx::DRAWABLE_TYPE as raw::c_int); - let surface_type = match surface_type { - SurfaceType::Window => ffi::glx::WINDOW_BIT, - SurfaceType::PBuffer => ffi::glx::PBUFFER_BIT, - SurfaceType::Surfaceless => ffi::glx::DONT_CARE, /* TODO: Properly support */ + 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, }; - out.push(surface_type as raw::c_int); - - // TODO: Use RGB/RGB_FLOAT_BIT_ARB if they don't want alpha bits, - // fallback to it if they don't care - out.push(ffi::glx::RENDER_TYPE as raw::c_int); - if pf_reqs.float_color_buffer { - if check_ext(extensions, "GLX_ARB_fbconfig_float") { - out.push(ffi::glx_extra::RGBA_FLOAT_BIT_ARB as raw::c_int); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } else { - out.push(ffi::glx::RGBA_BIT as raw::c_int); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(ffi::glx::RED_SIZE as raw::c_int); - out.push((color / 3) as raw::c_int); - out.push(ffi::glx::GREEN_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int); - out.push(ffi::glx::BLUE_SIZE as raw::c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(ffi::glx::ALPHA_SIZE as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(ffi::glx::DEPTH_SIZE as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(ffi::glx::STENCIL_SIZE as raw::c_int); - out.push(stencil as raw::c_int); - } - - let double_buffer = pf_reqs.double_buffer.unwrap_or(true); - out.push(ffi::glx::DOUBLEBUFFER as raw::c_int); - out.push(if double_buffer { 1 } else { 0 }); - - if let Some(multisampling) = pf_reqs.multisampling { - if check_ext(extensions, "GLX_ARB_multisample") { - out.push(ffi::glx_extra::SAMPLE_BUFFERS_ARB as raw::c_int); - out.push(if multisampling == 0 { 0 } else { 1 }); - out.push(ffi::glx_extra::SAMPLES_ARB as raw::c_int); - out.push(multisampling as raw::c_int); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } - out.push(ffi::glx::STEREO as raw::c_int); - out.push(if pf_reqs.stereoscopy { 1 } else { 0 }); - - if pf_reqs.srgb { - if check_ext(extensions, "GLX_ARB_framebuffer_sRGB") { - out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int); - out.push(1); - } else if check_ext(extensions, "GLX_EXT_framebuffer_sRGB") { - out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int); - out.push(1); - } else { - return Err(CreationError::NoAvailablePixelFormat); - } - } - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - if check_ext(extensions, "GLX_ARB_context_flush_control") { - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as raw::c_int); - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as raw::c_int); - } - } - } - - out.push(ffi::glx::CONFIG_CAVEAT as raw::c_int); - out.push(ffi::glx::DONT_CARE as raw::c_int); - - out.push(0); - out - }; - - // calling glXChooseFBConfig - let (fb_config, visual_infos): (ffi::glx::types::GLXFBConfig, ffi::XVisualInfo) = { - let mut num_configs = 0; - let configs = glx.ChooseFBConfig( - xconn.display as *mut _, - screen_id, - descriptor.as_ptr(), - &mut num_configs, + // 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 _, ); - if configs.is_null() { - return Err(CreationError::NoAvailablePixelFormat); - } - if num_configs == 0 { - return Err(CreationError::NoAvailablePixelFormat); - } - - match crate::platform_impl::x11_utils::select_config( - xconn, - transparent, - pf_reqs, - (0..num_configs).collect(), - |config_id| { - let visual_infos_raw = glx.GetVisualFromFBConfig( - xconn.display as *mut _, - *configs.offset(*config_id as isize), - ); - - if visual_infos_raw.is_null() { - return None; - } - - let visual_infos: ffi::XVisualInfo = std::ptr::read(visual_infos_raw as *const _); - (xconn.xlib.XFree)(visual_infos_raw as *mut _); - Some(visual_infos) - }, - ) { - Ok((config_id, visual_infos)) => { - let config = *configs.offset(config_id as isize); - let config = config; - - (xconn.xlib.XFree)(configs as *mut _); - (config, visual_infos) - } - Err(()) => { - (xconn.xlib.XFree)(configs as *mut _); - return Err(CreationError::NoAvailablePixelFormat); - } - } - }; - - let get_attrib = |attrib: raw::c_int| -> i32 { - let mut value = 0; - glx.GetFBConfigAttrib(xconn.display as *mut _, fb_config, attrib, &mut value); - // TODO: check return value - value - }; - - let pf_desc = PixelFormat { - hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as raw::c_int) - != ffi::glx::SLOW_CONFIG as raw::c_int, - color_bits: get_attrib(ffi::glx::RED_SIZE as raw::c_int) as u8 - + get_attrib(ffi::glx::GREEN_SIZE as raw::c_int) as u8 - + get_attrib(ffi::glx::BLUE_SIZE as raw::c_int) as u8, - alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as raw::c_int) as u8, - depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as raw::c_int) as u8, - stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as raw::c_int) as u8, - stereoscopy: get_attrib(ffi::glx::STEREO as raw::c_int) != 0, - double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as raw::c_int) != 0, - multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as raw::c_int) != 0 { - Some(get_attrib(ffi::glx::SAMPLES as raw::c_int) as u16) - } else { - None - }, - srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int) != 0 - || get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int) != 0, - }; + let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string(); - Ok((fb_config, pf_desc, visual_infos)) -} + *LAST_GLX_ERROR.lock().unwrap() = + Some(Error::new(Some(code as _), Some(description), kind)); -/// Checks if `ext` is available. -fn check_ext(extensions: &str, ext: &str) -> bool { - extensions.split(' ').any(|s| s == ext) + true + } } -fn load_extensions( - xconn: &Arc, - screen_id: raw::c_int, -) -> Result { +/// Get the error from the X11. +fn last_glx_error(display: display::GlxDisplay) -> Result<()> { unsafe { - let glx = GLX.as_ref().unwrap(); - let extensions = glx.QueryExtensionsString(xconn.display as *mut _, screen_id); - if extensions.is_null() { - return Err(CreationError::OsError( - "`glXQueryExtensionsString` found no glX extensions".to_string(), - )); - } - let extensions = CStr::from_ptr(extensions).to_bytes().to_vec(); - Ok(String::from_utf8(extensions).unwrap()) + // Force synchronization. + (XLIB.as_ref().unwrap().XSync)(*display as *mut _, 0); + } + + // Reset and report last error. + let last_error = LAST_GLX_ERROR.lock().unwrap().take(); + match last_error { + Some(error) => Err(error), + None => Ok(()), } } diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs new file mode 100644 index 0000000000..323cc1a478 --- /dev/null +++ b/glutin/src/api/glx/surface.rs @@ -0,0 +1,297 @@ +//! 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::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) => *xid, + _ => { + return Err( + ErrorKind::NotSupported("provided native pixmap is not supported.").into() + ) + }, + }; + + let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + let config = config.clone(); + let surface = self.inner.glx.CreatePixmap( + self.inner.raw.cast(), + *config.inner.raw, + xid, + attrs.as_ptr(), + ); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { + display: self.clone(), + config, + raw: surface, + _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 `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + let config = config.clone(); + let surface = + self.inner.glx.CreatePbuffer(self.inner.raw.cast(), *config.inner.raw, attrs.as_ptr()); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { + display: self.clone(), + config, + raw: surface, + _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) => window_handle.window, + _ => { + return Err( + ErrorKind::NotSupported("provided native window is not supported").into() + ) + }, + }; + + let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); + + // Push `glx::NONE` to terminate the list. + attrs.push(glx::NONE as c_int); + + let config = config.clone(); + let surface = self.inner.glx.CreateWindow( + self.inner.raw.cast(), + *config.inner.raw, + window, + attrs.as_ptr() as *const _, + ); + + super::last_glx_error(self.inner.raw)?; + + Ok(Surface { + display: self.clone(), + config, + raw: surface, + _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 Surface { + fn raw_attribute(&self, attr: c_int) -> c_uint { + unsafe { + let mut value = 0; + // This shouldn't generate any errors given that we know that the surface is + // valid. + self.display.inner.glx.QueryDrawable( + self.display.inner.raw.cast(), + self.raw, + attr, + &mut value, + ); + value + } + } +} + +impl Drop for Surface { + fn drop(&mut self) { + unsafe { + match T::surface_type() { + SurfaceType::Pbuffer => { + self.display.inner.glx.DestroyPbuffer(self.display.inner.raw.cast(), self.raw); + }, + SurfaceType::Window => { + self.display.inner.glx.DestroyWindow(self.display.inner.raw.cast(), self.raw); + }, + SurfaceType::Pixmap => { + self.display.inner.glx.DestroyPixmap(self.display.inner.raw.cast(), self.raw); + }, + } + } + } +} + +impl GlSurface for Surface { + type Context = PossiblyCurrentContext; + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) as u32 + } + + fn width(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + fn height(&self) -> Option { + Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + } + + fn is_single_buffered(&self) -> bool { + self.config.is_singe_buffered() + } + + fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { + unsafe { + self.display.inner.glx.SwapBuffers(self.display.inner.raw.cast(), self.raw); + super::last_glx_error(self.display.inner.raw) + } + } + + fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { + let interval = match interval { + SwapInterval::DontWait => 0, + SwapInterval::Wait(n) => n.get(), + }; + + let res = match self.display.inner.glx_extra { + Some(extra) + if self.display.inner.client_extensions.contains("GLX_EXT_swap_control") => + unsafe { + extra.SwapIntervalEXT(*self.display.inner.raw.cast(), self.raw, interval as _); + // Check for error explicitly here, other apis do have indication for failure. + 0 + }, + Some(extra) + if self.display.inner.client_extensions.contains("GLX_SGI_swap_control") => + unsafe { extra.SwapIntervalSGI(interval as _) }, + Some(extra) + if self.display.inner.client_extensions.contains("GLX_MESA_swap_control") => + unsafe { extra.SwapIntervalMESA(interval as _) }, + _ => { + return Err( + ErrorKind::NotSupported("swap contol extrensions are not supported").into() + ) + }, + }; + + if res == 0 { + super::last_glx_error(self.display.inner.raw) + } 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 { 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 {} diff --git a/glutin/src/api/ios/mod.rs b/glutin/src/api/ios/mod.rs deleted file mode 100644 index cc40fc01bf..0000000000 --- a/glutin/src/api/ios/mod.rs +++ /dev/null @@ -1,450 +0,0 @@ -#![cfg(target_os = "ios")] -#![allow(clippy::let_unit_value)] -//! iOS support -//! -//! # Building app -//! To build ios app you will need rustc built for this targets: -//! -//! - armv7-apple-ios -//! - armv7s-apple-ios -//! - i386-apple-ios -//! - aarch64-apple-ios -//! - x86_64-apple-ios -//! -//! Then -//! -//! ``` -//! cargo build --target=... -//! ``` -//! The simplest way to integrate your app into xcode environment is to build it -//! as a static library. Wrap your main function and export it. -//! -//! ```rust, ignore -//! #[no_mangle] -//! pub extern fn start_glutin_app() { -//! start_inner() -//! } -//! -//! fn start_inner() { -//! ... -//! } -//! ``` -//! -//! Compile project and then drag resulting .a into Xcode project. Add glutin.h -//! to xcode. -//! -//! ```c -//! void start_glutin_app(); -//! ``` -//! -//! Use start_glutin_app inside your xcode's main function. -//! -//! -//! # App lifecycle and events -//! -//! iOS environment is very different from other platforms and you must be very -//! careful with it's events. Familiarize yourself with [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/). -//! -//! -//! This is how those event are represented in glutin: -//! -//! - applicationDidBecomeActive is Focused(true) -//! - applicationWillResignActive is Focused(false) -//! - applicationDidEnterBackground is Suspended(true) -//! - applicationWillEnterForeground is Suspended(false) -//! - applicationWillTerminate is Destroyed -//! -//! Keep in mind that after Destroyed event is received every attempt to draw -//! with opengl will result in segfault. -//! -//! Also note that app will not receive Destroyed event if suspended, it will be -//! SIGKILL'ed - -use crate::platform::ios::{WindowBuilderExtIOS, WindowExtIOS}; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, Rect, -}; - -use glutin_gles2_sys as ffi; -use objc::declare::ClassDecl; -use objc::runtime::{Class, Object, Sel, BOOL, NO, YES}; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::WindowBuilder; - -use std::ffi::CString; -use std::os::raw; - -#[derive(Debug, PartialEq)] -enum ColorFormat { - Rgba8888 = 0, - Rgb565 = 1, - Srgba8888 = 2, -} - -impl ColorFormat { - #[allow(non_upper_case_globals)] - pub fn for_view(view: ffi::id) -> Self { - let color_format: ffi::NSUInteger = unsafe { msg_send![view, drawableColorFormat] }; - match color_format { - ffi::GLKViewDrawableColorFormatRGBA8888 => ColorFormat::Rgba8888, - ffi::GLKViewDrawableColorFormatRGB565 => ColorFormat::Rgb565, - ffi::GLKViewDrawableColorFormatSRGBA8888 => ColorFormat::Srgba8888, - _ => unreachable!(), - } - } - - pub fn color_bits(&self) -> u8 { - if *self == ColorFormat::Rgba8888 || *self == ColorFormat::Srgba8888 { - 8 - } else { - 16 - } - } - - pub fn alpha_bits(&self) -> u8 { - if *self == ColorFormat::Rgba8888 || *self == ColorFormat::Srgba8888 { - 8 - } else { - 0 - } - } - - pub fn srgb(&self) -> bool { - *self == ColorFormat::Srgba8888 - } -} - -#[allow(non_upper_case_globals)] -fn depth_for_view(view: ffi::id) -> u8 { - let depth_format: ffi::NSUInteger = unsafe { msg_send![view, drawableDepthFormat] }; - match depth_format { - ffi::GLKViewDrawableDepthFormatNone => 0, - ffi::GLKViewDrawableDepthFormat16 => 16, - ffi::GLKViewDrawableDepthFormat24 => 24, - _ => unreachable!(), - } -} - -#[allow(non_upper_case_globals)] -fn stencil_for_view(view: ffi::id) -> u8 { - let stencil_format: ffi::NSUInteger = unsafe { msg_send![view, drawableStencilFormat] }; - match stencil_format { - ffi::GLKViewDrawableStencilFormatNone => 0, - ffi::GLKViewDrawableStencilFormat8 => 8, - _ => unreachable!(), - } -} - -#[allow(non_upper_case_globals)] -fn multisampling_for_view(view: ffi::id) -> Option { - let ms_format: ffi::NSUInteger = unsafe { msg_send![view, drawableMultisample] }; - match ms_format { - ffi::GLKViewDrawableMultisampleNone => None, - ffi::GLKViewDrawableMultisample4X => Some(4), - _ => unreachable!(), - } -} - -#[derive(Debug)] -pub struct Context { - eagl_context: ffi::id, - view: ffi::id, // this will be invalid after the `EventLoop` is dropped -} - -fn validate_version(version: u8) -> Result { - let version = version as ffi::NSUInteger; - if (ffi::kEAGLRenderingAPIOpenGLES1..=ffi::kEAGLRenderingAPIOpenGLES3).contains(&version) { - Ok(version) - } else { - Err(CreationError::OsError(format!( - "Specified OpenGL ES version ({:?}) is not availble on iOS. Only 1, 2, and 3 are valid options", - version, - ))) - } -} - -impl Context { - #[inline] - pub fn new_windowed( - builder: WindowBuilder, - el: &EventLoopWindowTarget, - _: &PixelFormatRequirements, - gl_attrs: &GlAttributes<&Context>, - ) -> Result<(winit::window::Window, Self), CreationError> { - create_view_class(); - let view_class = Class::get("MainGLView").expect("Failed to get class `MainGLView`"); - let builder = builder.with_root_view_class(view_class as *const _ as *const _); - if gl_attrs.sharing.is_some() { - unimplemented!("Shared contexts are unimplemented on iOS."); - } - let version = match gl_attrs.version { - GlRequest::Latest => ffi::kEAGLRenderingAPIOpenGLES3, - GlRequest::Specific(api, (major, _minor)) => { - if api == Api::OpenGlEs { - validate_version(major)? - } else { - return Err(CreationError::OsError(format!( - "Specified API ({:?}) is not availble on iOS. Only `Api::OpenGlEs` can be used", - api, - ))); - } - } - GlRequest::GlThenGles { opengles_version: (major, _minor), .. } => { - validate_version(major)? - } - }; - let win = builder.build(el)?; - let context = unsafe { - let eagl_context = Context::create_context(version)?; - let view = win.ui_view() as ffi::id; - let mut context = Context { eagl_context, view }; - context.init_context(&win); - context - }; - Ok((win, context)) - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - let wb = winit::window::WindowBuilder::new().with_visible(false).with_inner_size(size); - Self::new_windowed(wb, el, pf_reqs, gl_attr).map(|(_window, context)| context) - } - - unsafe fn create_context(mut version: ffi::NSUInteger) -> Result { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let eagl_context: ffi::id = msg_send![context_class, alloc]; - let mut valid_context = ffi::nil; - while valid_context == ffi::nil && version > 0 { - valid_context = msg_send![eagl_context, initWithAPI: version]; - version -= 1; - } - if valid_context == ffi::nil { - Err(CreationError::OsError( - "Failed to create an OpenGL ES context with any version".to_string(), - )) - } else { - Ok(eagl_context) - } - } - - unsafe fn init_context(&mut self, win: &winit::window::Window) { - let dict_class = Class::get("NSDictionary").expect("Failed to get class `NSDictionary`"); - let number_class = Class::get("NSNumber").expect("Failed to get class `NSNumber`"); - let draw_props: ffi::id = msg_send![dict_class, alloc]; - let draw_props: ffi::id = msg_send![draw_props, - initWithObjects: - vec![ - msg_send![number_class, numberWithBool:NO], - ffi::kEAGLColorFormatRGB565, - ].as_ptr() - forKeys: - vec![ - ffi::kEAGLDrawablePropertyRetainedBacking, - ffi::kEAGLDrawablePropertyColorFormat, - ].as_ptr() - count: 2 - ]; - self.make_current().unwrap(); - - let view = self.view; - let scale_factor = win.scale_factor() as ffi::CGFloat; - let _: () = msg_send![view, setContentScaleFactor: scale_factor]; - let layer: ffi::id = msg_send![view, layer]; - let _: () = msg_send![layer, setContentsScale: scale_factor]; - let _: () = msg_send![layer, setDrawableProperties: draw_props]; - - let gl = ffi::gles::Gles2::load_with(|symbol| { - self.get_proc_address(symbol) as *const raw::c_void - }); - let mut color_render_buf: ffi::gles::types::GLuint = 0; - let mut frame_buf: ffi::gles::types::GLuint = 0; - gl.GenRenderbuffers(1, &mut color_render_buf); - gl.BindRenderbuffer(ffi::gles::RENDERBUFFER, color_render_buf); - - let ok: BOOL = msg_send![self.eagl_context, renderbufferStorage:ffi::gles::RENDERBUFFER fromDrawable:layer]; - if ok != YES { - panic!("EAGL: could not set renderbufferStorage"); - } - - gl.GenFramebuffers(1, &mut frame_buf); - gl.BindFramebuffer(ffi::gles::FRAMEBUFFER, frame_buf); - - gl.FramebufferRenderbuffer( - ffi::gles::FRAMEBUFFER, - ffi::gles::COLOR_ATTACHMENT0, - ffi::gles::RENDERBUFFER, - color_render_buf, - ); - - let status = gl.CheckFramebufferStatus(ffi::gles::FRAMEBUFFER); - if gl.CheckFramebufferStatus(ffi::gles::FRAMEBUFFER) != ffi::gles::FRAMEBUFFER_COMPLETE { - panic!("framebuffer status: {:?}", status); - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - unsafe { - let res: BOOL = - msg_send![self.eagl_context, presentRenderbuffer: ffi::gles::RENDERBUFFER]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext presentRenderbuffer` failed", - ))) - } - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - 0 - } - - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - let color_format = ColorFormat::for_view(self.view); - PixelFormat { - hardware_accelerated: true, - color_bits: color_format.color_bits(), - alpha_bits: color_format.alpha_bits(), - depth_bits: depth_for_view(self.view), - stencil_bits: stencil_for_view(self.view), - stereoscopy: false, - double_buffer: true, - multisampling: multisampling_for_view(self.view), - srgb: color_format.srgb(), - } - } - - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // N/A - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let res: BOOL = msg_send![context_class, setCurrentContext: self.eagl_context]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext setCurrentContext` failed", - ))) - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if !self.is_current() { - return Ok(()); - } - - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let res: BOOL = msg_send![context_class, setCurrentContext: ffi::nil]; - if res == YES { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "`EAGLContext setCurrentContext` failed", - ))) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - // TODO: This can likely be implemented using - // `currentContext`/`getCurrentContext` - true - } - - #[inline] - pub fn get_proc_address(&self, proc_name: &str) -> *const core::ffi::c_void { - let proc_name_c = CString::new(proc_name).expect("proc name contained interior nul byte"); - let path = b"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\0"; - - unsafe { - let lib = - ffi::dlopen(path.as_ptr() as *const raw::c_char, ffi::RTLD_LAZY | ffi::RTLD_GLOBAL); - ffi::dlsym(lib, proc_name_c.as_ptr()) as *const _ - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - self.eagl_context as *mut raw::c_void - } - - #[inline] - pub fn get_api(&self) -> Api { - Api::OpenGlEs - } -} - -fn create_view_class() { - extern "C" fn init_with_frame(this: &Object, _: Sel, frame: ffi::CGRect) -> ffi::id { - unsafe { - let view: ffi::id = msg_send![super(this, class!(GLKView)), initWithFrame: frame]; - - let mask = ffi::UIViewAutoresizingFlexibleWidth | ffi::UIViewAutoresizingFlexibleHeight; - let _: () = msg_send![view, setAutoresizingMask: mask]; - let _: () = msg_send![view, setAutoresizesSubviews: YES]; - - let layer: ffi::id = msg_send![view, layer]; - let _: () = msg_send![layer, setOpaque: YES]; - - view - } - } - - extern "C" fn layer_class(_: &Class, _: Sel) -> *const Class { - Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`") - as *const objc::runtime::Class - } - - let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`"); - let mut decl = - ClassDecl::new("MainGLView", superclass).expect("Failed to declare class `MainGLView`"); - unsafe { - decl.add_method( - sel!(initWithFrame:), - init_with_frame as extern "C" fn(&Object, Sel, ffi::CGRect) -> ffi::id, - ); - decl.add_class_method( - sel!(layerClass), - layer_class as extern "C" fn(&Class, Sel) -> *const Class, - ); - decl.register(); - } -} - -impl Drop for Context { - fn drop(&mut self) { - let _: () = unsafe { msg_send![self.eagl_context, release] }; - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} diff --git a/glutin/src/api/mod.rs b/glutin/src/api/mod.rs index 8f438ccd1f..f3b8459c6b 100644 --- a/glutin/src/api/mod.rs +++ b/glutin/src/api/mod.rs @@ -1,6 +1,10 @@ -pub mod dlloader; +//! The underlying OpenGL platform Api. + +#[cfg(cgl_backend)] +pub mod cgl; +#[cfg(egl_backend)] pub mod egl; +#[cfg(glx_backend)] pub mod glx; -pub mod ios; -pub mod osmesa; +#[cfg(wgl_backend)] pub mod wgl; diff --git a/glutin/src/api/osmesa/mod.rs b/glutin/src/api/osmesa/mod.rs deleted file mode 100644 index 13035f98ed..0000000000 --- a/glutin/src/api/osmesa/mod.rs +++ /dev/null @@ -1,224 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -pub mod ffi { - pub use osmesa_sys::OSMesaContext; -} - -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormatRequirements, - Robustness, -}; - -use winit::dpi; - -use std::ffi::CString; -use std::os::raw; - -#[derive(Debug)] -pub struct OsMesaContext { - context: osmesa_sys::OSMesaContext, - buffer: Vec, - width: u32, - height: u32, -} - -#[derive(Debug)] -struct NoEsOrWebGlSupported; - -impl std::fmt::Display for NoEsOrWebGlSupported { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "OsMesa only works with desktop OpenGL; OpenGL ES or WebGL are not supported") - } -} - -impl std::error::Error for NoEsOrWebGlSupported { - fn description(&self) -> &str { - "OsMesa only works with desktop OpenGL" - } -} - -#[derive(Debug)] -struct LoadingError(String); - -impl LoadingError { - fn new(d: D) -> Self { - LoadingError(format!("{:?}", d)) - } -} - -impl std::fmt::Display for LoadingError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "Failed to load OsMesa dynamic library: {}", self.0) - } -} - -impl std::error::Error for LoadingError { - fn description(&self) -> &str { - "The library or a symbol of it could not be loaded" - } -} - -impl OsMesaContext { - pub fn new( - _pf_reqs: &PixelFormatRequirements, - opengl: &GlAttributes<&OsMesaContext>, - size: dpi::PhysicalSize, - ) -> Result { - osmesa_sys::OsMesa::try_loading() - .map_err(LoadingError::new) - .map_err(|e| CreationError::NoBackendAvailable(Box::new(e)))?; - - if opengl.sharing.is_some() { - panic!("Context sharing not possible with OsMesa") - } - - match opengl.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - - // TODO: use `pf_reqs` for the format - - let mut attribs = Vec::new(); - - if let Some(profile) = opengl.profile { - attribs.push(osmesa_sys::OSMESA_PROFILE); - - match profile { - GlProfile::Compatibility => { - attribs.push(osmesa_sys::OSMESA_COMPAT_PROFILE); - } - GlProfile::Core => { - attribs.push(osmesa_sys::OSMESA_CORE_PROFILE); - } - } - } - - match opengl.version { - GlRequest::Latest => {} - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); - attribs.push(major as raw::c_int); - attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); - attribs.push(minor as raw::c_int); - } - GlRequest::Specific(Api::OpenGlEs, _) | GlRequest::Specific(Api::WebGl, _) => { - return Err(CreationError::NoBackendAvailable(Box::new(NoEsOrWebGlSupported))); - } - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); - attribs.push(major as raw::c_int); - attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); - attribs.push(minor as raw::c_int); - } - } - - // attribs array must be NULL terminated. - attribs.push(0); - - let size: (u32, u32) = size.into(); - - Ok(OsMesaContext { - width: size.0, - height: size.1, - buffer: std::iter::repeat(unsafe { std::mem::zeroed() }) - .take((size.0 * size.1) as usize) - .collect(), - context: unsafe { - let ctx = - osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), std::ptr::null_mut()); - if ctx.is_null() { - return Err(CreationError::OsError( - "OSMesaCreateContextAttribs failed".to_string(), - )); - } - ctx - }, - }) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let ret = osmesa_sys::OSMesaMakeCurrent( - self.context, - self.buffer.as_ptr() as *mut _, - 0x1401, - self.width as raw::c_int, - self.height as raw::c_int, - ); - - // an error can only happen in case of invalid parameter, which would - // indicate a bug in glutin - if ret == 0 { - panic!("OSMesaMakeCurrent failed"); - } - - Ok(()) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if osmesa_sys::OSMesaGetCurrentContext() == self.context { - // Supported with the non-gallium drivers, but not the gallium ones - // I (gentz) have filed a patch upstream to mesa to correct this, - // however, older users (or anyone not running mesa-git, tbh) - // probably won't support this. - // - // There is no way to tell, ofc, without just calling the function - // and seeing if it work. - // - // https://gitlab.freedesktop.org/mesa/mesa/merge_requests/533 - let ret = - osmesa_sys::OSMesaMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut(), 0, 0, 0); - - if ret == 0 { - unimplemented!( - "OSMesaMakeCurrent failed to make the context not current. This most likely means that you're using an older gallium-based mesa driver." - ) - } - } - - Ok(()) - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context } - } - - #[inline] - pub fn get_api(&self) -> Api { - Api::OpenGl - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - self.context as *mut _ - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - unsafe { - let c_str = CString::new(addr.as_bytes().to_vec()).unwrap(); - core::mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr() as *mut _)) - } - } -} - -impl Drop for OsMesaContext { - #[inline] - fn drop(&mut self) { - unsafe { osmesa_sys::OSMesaDestroyContext(self.context) } - } -} - -unsafe impl Send for OsMesaContext {} -unsafe impl Sync for OsMesaContext {} diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs new file mode 100644 index 0000000000..d3f2cf63df --- /dev/null +++ b/glutin/src/api/wgl/config.rs @@ -0,0 +1,501 @@ +//! 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::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; + +// Multisampling extension. +const MULTI_SAMPLE_ARB: &str = "WGL_ARB_multisample"; + +// Srgb extensions. +const SRGB_ARB: &str = "WGL_ARB_framebuffer_sRGB"; +const SRGB_EXT: &str = "WGL_ARB_framebuffer_sRGB"; + +impl Display { + pub(crate) fn find_configs( + &self, + template: ConfigTemplate, + ) -> Result + '_>> { + let hwnd = match template.native_window { + Some(RawWindowHandle::Win32(window_handle)) => window_handle.hwnd 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, + }; + + 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, + 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.client_extensions.contains("WGL_ARB_pixel_format_float") + && 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 self.inner.client_extensions.contains(MULTI_SAMPLE_ARB) { + attrs.push(wgl_extra::SAMPLE_BUFFERS_ARB as c_int); + attrs.push((template.sample_buffers != 0) as c_int); + attrs.push(wgl_extra::SAMPLES_ARB as c_int); + attrs.push(template.sample_buffers 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 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.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) => gdi::GetDC(window.hwnd as _), + _ => return Err(ErrorKind::BadNativeWindow.into()), + }; + + let descriptor = + self.inner.descriptor.as_ref().map(|desc| desc as _).unwrap_or(std::ptr::null()); + 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 => self.raw_attribute(wgl_extra::DOUBLE_BUFFER_ARB as c_int) == 0, + } + } + + 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) -> ColorBufferType { + let (r_size, g_size, b_size) = match self.inner.descriptor.as_ref() { + Some(descriptor) => (descriptor.cRedBits, descriptor.cGreenBits, descriptor.cBlueBits), + _ => { + 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) + }, + }; + + ColorBufferType::Rgb { r_size, g_size, b_size } + } + + fn float_pixels(&self) -> bool { + 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, + _ => 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.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + } else if self.inner.display.inner.client_extensions.contains(SRGB_ARB) { + 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, + _ => 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, + _ => self.raw_attribute(wgl_extra::STENCIL_BITS_ARB as c_int) as _, + } + } + + fn sample_buffers(&self) -> u8 { + if self.inner.display.inner.client_extensions.contains(MULTI_SAMPLE_ARB) { + 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; + } + }, + _ => { + 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 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::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, + 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)) + } +} diff --git a/glutin/src/api/wgl/context.rs b/glutin/src/api/wgl/context.rs new file mode 100644 index 0000000000..fec734f9f6 --- /dev/null +++ b/glutin/src/api/wgl/context.rs @@ -0,0 +1,404 @@ +//! WGL context handling. + +use std::ffi::{self, CStr}; +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 windows_sys::Win32::System::LibraryLoader as dll_loader; + +use crate::config::GetGlConfig; +use crate::context::{ + AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, ReleaseBehaviour, + Robustness, +}; +use crate::display::GetGlDisplay; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::SurfaceTypeTrait; + +use super::config::Config; +use super::display::Display; +use super::surface::Surface; + +impl Display { + pub(crate) fn create_context( + &self, + config: &Config, + context_attributes: &ContextAttributes, + ) -> Result { + let 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 as _) + }, + _ => config.inner.hdc, + }; + + let share_ctx = match context_attributes.shared_context { + Some(RawContext::Wgl(share)) => share, + _ => std::ptr::null(), + }; + + let context = 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) + } + }; + + let config = config.clone(); + let inner = ContextInner { display: self.clone(), config, raw: context }; + Ok(NotCurrentContext { inner }) + } + + fn create_context_arb( + &self, + hdc: HDC, + share_context: HGLRC, + context_attributes: &ContextAttributes, + ) -> Result { + 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.client_extensions.contains("WGL_EXT_create_context_es2_profile") + || self.inner.client_extensions.contains("WGL_EXT_create_context_es_profile"); + + let (profile, version) = match context_attributes.api { + ContextApi::OpenGl(version) => { + let profile = context_attributes.profile.map(|profile| match profile { + GlProfile::Core => wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + GlProfile::Compatibility => wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + }); + (profile, version) + }, + ContextApi::Gles(version) if supports_es => { + (Some(wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT), version) + }, + _ => { + 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; + if self.inner.client_extensions.contains("WGL_ARB_create_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.client_extensions.contains("WGL_ARB_create_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); + }, + } + } 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 { + 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.client_extensions.contains("WGL_ARB_context_flush_control") { + match context_attributes.release_behavior { + ReleaseBehaviour::Flush => { + attrs.push(wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); + attrs.push(wgl_extra::CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB as c_int); + }, + ReleaseBehaviour::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 != ReleaseBehaviour::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)) + } + } + } +} + +/// 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, +} + +/// 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 PossiblyCurrentContext {} +impl Sealed for NotCurrentContext {} + +impl NotCurrentContext { + fn new(inner: ContextInner) -> Self { + Self { inner } + } +} + +impl GetGlDisplay for NotCurrentContext { + type Target = Display; + + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlDisplay for PossiblyCurrentContext { + type Target = Display; + + fn display(&self) -> Self::Target { + self.inner.display.clone() + } +} + +impl GetGlConfig for NotCurrentContext { + type Target = Config; + + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl GetGlConfig for PossiblyCurrentContext { + type Target = Config; + + fn config(&self) -> Self::Target { + self.inner.config.clone() + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + self.inner.make_current(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 PossiblyCurrentGlContext for PossiblyCurrentContext { + type NotCurrentContext = NotCurrentContext; + + fn make_not_current(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(NotCurrentContext::new(self.inner)) + } + } + + fn is_current(&self) -> bool { + unsafe { wgl::GetCurrentContext() == *self.inner.raw } + } + + 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.display.inner.lib_opengl32, addr.cast()) + .map_or(std::ptr::null(), |fn_ptr| fn_ptr as *const _) + } + } + } +} + +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> PossiblyCurrentContext { + PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } + } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; + + 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 AsRawContext for PossiblyCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Wgl(*self.inner.raw) + } +} + +impl AsRawContext for NotCurrentContext { + fn raw_context(&self) -> RawContext { + RawContext::Wgl(*self.inner.raw) + } +} + +struct ContextInner { + display: Display, + config: Config, + raw: WglContext, +} + +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_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 { + let hdc = gdi::GetDC(surface.hwnd); + if wgl::MakeCurrent(hdc as _, self.raw.cast()) == 0 { + Err(IoError::last_os_error().into()) + } else { + Ok(()) + } + } + } +} + +impl Drop for ContextInner { + fn drop(&mut self) { + unsafe { + wgl::DeleteContext(*self.raw); + } + } +} diff --git a/glutin/src/api/wgl/display.rs b/glutin/src/api/wgl/display.rs new file mode 100644 index 0000000000..0ca78e8ffc --- /dev/null +++ b/glutin/src/api/wgl/display.rs @@ -0,0 +1,159 @@ +//! WGL display initialization and extension loading. + +use std::collections::HashSet; +use std::ffi::{CStr, OsStr}; +use std::fmt; +use std::os::windows::ffi::OsStrExt; +use std::sync::Arc; + +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; +use windows_sys::Win32::Foundation::HINSTANCE; +use windows_sys::Win32::Graphics::Gdi::HDC; +use windows_sys::Win32::System::LibraryLoader as dll_loader; + +use crate::config::ConfigTemplate; +use crate::display::{AsRawDisplay, RawDisplay}; +use crate::error::{ErrorKind, Result}; +use crate::prelude::*; +use crate::private::Sealed; +use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; + +use super::config::Config; +use super::context::NotCurrentContext; +use super::surface::Surface; +use super::WglExtra; + +/// 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. + /// + /// # Safety + /// + /// The `native_window` must point to the valid platform window and have + /// valid `hinstance`. + pub unsafe fn from_raw( + display: RawDisplayHandle, + native_window: Option, + ) -> Result { + if !matches!(display, RawDisplayHandle::Windows(..)) { + return Err(ErrorKind::NotSupported("provided native display is not supported").into()); + } + + let name = + OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()).collect::>(); + let lib_opengl32 = 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 { + let (wgl_extra, client_extensions) = super::load_extra_functions(window.hwnd as _)?; + (Some(wgl_extra), client_extensions) + } else { + (None, HashSet::new()) + }; + + let inner = Arc::new(DisplayInner { lib_opengl32, wgl_extra, client_extensions }); + Ok(Display { 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 + '_>> { + Self::find_configs(self, template) + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_window_surface(self, config, surface_attributes) + } + + unsafe fn create_pbuffer_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pbuffer_surface(self, config, surface_attributes) + } + + unsafe fn create_context( + &self, + config: &Self::Config, + context_attributes: &crate::context::ContextAttributes, + ) -> Result { + Self::create_context(self, config, context_attributes) + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + Self::create_pixmap_surface(self, config, surface_attributes) + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + RawDisplay::Wgl + } +} + +impl Sealed for Display {} + +pub(crate) struct DisplayInner { + /// Client WGL extensions. + pub(crate) lib_opengl32: HINSTANCE, + + /// Extra functions used by the impl. + pub(crate) wgl_extra: Option<&'static WglExtra>, + + 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("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() + } +} diff --git a/glutin/src/api/wgl/make_current_guard.rs b/glutin/src/api/wgl/make_current_guard.rs deleted file mode 100644 index c343ec2c67..0000000000 --- a/glutin/src/api/wgl/make_current_guard.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::gl; -use crate::CreationError; - -use winapi::shared::windef::{HDC, HGLRC}; - -use std::io; -use std::marker::PhantomData; -use std::os::raw; - -/// A guard for when you want to make the context current. Destroying the guard -/// restores the previously-current context. -#[derive(Debug)] -pub struct CurrentContextGuard<'a, 'b> { - previous_hdc: HDC, - previous_hglrc: HGLRC, - marker1: PhantomData<&'a ()>, - marker2: PhantomData<&'b ()>, -} - -impl<'a, 'b> CurrentContextGuard<'a, 'b> { - pub unsafe fn make_current( - hdc: HDC, - context: HGLRC, - ) -> Result, CreationError> { - let previous_hdc = gl::wgl::GetCurrentDC() as HDC; - let previous_hglrc = gl::wgl::GetCurrentContext() as HGLRC; - - let result = gl::wgl::MakeCurrent(hdc as *const _, context as *const _); - if result == 0 { - return Err(CreationError::OsError(format!( - "wglMakeCurrent function failed: {}", - io::Error::last_os_error() - ))); - } - - Ok(CurrentContextGuard { - previous_hdc, - previous_hglrc, - marker1: PhantomData, - marker2: PhantomData, - }) - } -} - -impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { - fn drop(&mut self) { - unsafe { - gl::wgl::MakeCurrent( - self.previous_hdc as *const raw::c_void, - self.previous_hglrc as *const raw::c_void, - ); - } - } -} diff --git a/glutin/src/api/wgl/mod.rs b/glutin/src/api/wgl/mod.rs index ad83595eae..2b011c51c7 100644 --- a/glutin/src/api/wgl/mod.rs +++ b/glutin/src/api/wgl/mod.rs @@ -1,898 +1,133 @@ -#![cfg(any(target_os = "windows"))] +//! WGL Api. -mod make_current_guard; - -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, PixelFormat, - PixelFormatRequirements, ReleaseBehavior, Robustness, -}; - -use self::make_current_guard::CurrentContextGuard; - -use glutin_wgl_sys as gl; -use winapi::shared::minwindef::HMODULE; -use winapi::shared::minwindef::*; -use winapi::shared::ntdef::LPCWSTR; -use winapi::shared::windef::{HDC, HGLRC, HWND}; -use winapi::um::libloaderapi::*; -use winapi::um::wingdi::*; -use winapi::um::winuser::*; - -use std::ffi::{CStr, CString, OsStr}; -use std::os::raw; +use std::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; -/// A WGL context. -/// -/// Note: should be destroyed before its window. -#[derive(Debug)] -pub struct Context { - context: ContextWrapper, +use glutin_wgl_sys::{wgl, wgl_extra}; +use once_cell::sync::OnceCell; +use windows_sys::Win32::Foundation::HWND; +use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; +use windows_sys::Win32::System::LibraryLoader as dll_loader; +use windows_sys::Win32::UI::WindowsAndMessaging::{self as wm, WINDOWPLACEMENT, WNDCLASSEXW}; - hdc: HDC, +use crate::error::{Error, ErrorKind, Result}; - /// Bound to `opengl32.dll`. - /// - /// `wglGetProcAddress` returns null for GL 1.1 functions because they are - /// already defined by the system. This module contains them. - gl_library: HMODULE, +pub mod config; +pub mod context; +pub mod display; +pub mod surface; - /// The pixel format that has been used to create this context. - pixel_format: PixelFormat, -} - -/// A simple wrapper that destroys the window when it is destroyed. -#[derive(Debug)] -struct WindowWrapper(HWND, HDC); +pub(crate) static WGL_EXTRA: OnceCell = OnceCell::new(); -impl Drop for WindowWrapper { - #[inline] - fn drop(&mut self) { - unsafe { - DestroyWindow(self.0); - } - } -} +pub(crate) struct WglExtra(wgl_extra::Wgl); -/// Wraps around a context so that it is destroyed when necessary. -#[derive(Debug)] -struct ContextWrapper(HGLRC); +unsafe impl Send for WglExtra {} +unsafe impl Sync for WglExtra {} -impl Drop for ContextWrapper { - #[inline] - fn drop(&mut self) { - unsafe { - gl::wgl::DeleteContext(self.0 as *const _); - } +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 Context { - /// Attempt to build a new WGL context on a window. - /// - /// # Unsafety - /// - /// The `window` must continue to exist as long as the resulting [`Context`] - /// exists. - #[inline] - pub unsafe fn new( - pf_reqs: &PixelFormatRequirements, - opengl: &GlAttributes, - win: HWND, - ) -> Result { - let hdc = GetDC(win); - if hdc.is_null() { - let err = Err(CreationError::OsError(format!( - "GetDC function failed: {}", - std::io::Error::last_os_error() - ))); - return err; - } - - // loading the functions that are not guaranteed to be supported - let extra_functions = load_extra_functions(win)?; - - // getting the list of the supported extensions - let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() { - let data = extra_functions.GetExtensionsStringARB(hdc as *const _); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - } else if extra_functions.GetExtensionsStringEXT.is_loaded() { - let data = extra_functions.GetExtensionsStringEXT(); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - } else { - String::new() - }; - - let use_arb_for_pixel_format = extensions.split(' ').any(|i| i == "WGL_ARB_pixel_format"); - - // calling SetPixelFormat, if not already done - let mut pixel_format_id = GetPixelFormat(hdc); - if pixel_format_id == 0 { - let id = if use_arb_for_pixel_format { - choose_arb_pixel_format_id(&extra_functions, &extensions, hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - } else { - choose_native_pixel_format_id(hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - }; - - set_pixel_format(hdc, id)?; - pixel_format_id = id; - } - - let pixel_format = if use_arb_for_pixel_format { - choose_arb_pixel_format(&extra_functions, &extensions, hdc, pixel_format_id) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - } else { - choose_native_pixel_format(hdc, pf_reqs, pixel_format_id) - .map_err(|_| CreationError::NoAvailablePixelFormat)? - }; - - // creating the OpenGL context - let context = - create_context(Some((&extra_functions, pf_reqs, opengl, &extensions)), win, hdc)?; - - // loading the opengl32 module - let gl_library = load_opengl32_dll()?; +impl Deref for WglExtra { + type Target = wgl_extra::Wgl; - // handling vsync - if extensions.split(' ').any(|i| i == "WGL_EXT_swap_control") { - let _guard = CurrentContextGuard::make_current(hdc, context.0)?; - - if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) == 0 { - return Err(CreationError::OsError("wglSwapIntervalEXT failed".to_string())); - } - } - - Ok(Context { context, hdc, gl_library, pixel_format }) - } - - /// Returns the raw HGLRC. - #[inline] - pub fn get_hglrc(&self) -> HGLRC { - self.context.0 - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::last_os_error())) - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if self.is_current() && gl::wgl::MakeCurrent(self.hdc as *const _, std::ptr::null()) != 0 { - Ok(()) - } else { - Err(ContextError::IoError(std::io::Error::last_os_error())) - } - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const raw::c_void } - } - - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - - unsafe { - let p = gl::wgl::GetProcAddress(addr) as *const core::ffi::c_void; - if !p.is_null() { - return p; - } - GetProcAddress(self.gl_library, addr) as *const _ - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - // TODO: decide how to handle the error - // if unsafe { SwapBuffers(self.hdc) } != 0 { - // Ok(()) - // } else { - // Err(ContextError::IoError(std::io::Error::last_os_error())) - // } - unsafe { SwapBuffers(self.hdc) }; - Ok(()) - } - - #[inline] - pub fn get_api(&self) -> Api { - // FIXME: can be opengl es - Api::OpenGl - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() + fn deref(&self) -> &Self::Target { + &self.0 } } -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -/// Creates an OpenGL context. -/// -/// If `extra` is [`Some`], this function will attempt to use the latest WGL -/// functions to create the context. -/// -/// Otherwise, only the basic API will be used and the chances of -/// `CreationError::NotSupported` being returned increase. -unsafe fn create_context( - extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequirements, &GlAttributes, &str)>, - _: HWND, - hdc: HDC, -) -> Result { - let share; - - if let Some((extra_functions, _pf_reqs, opengl, extensions)) = extra { - share = opengl.sharing.unwrap_or(std::ptr::null_mut()); - - if extensions.split(' ').any(|i| i == "WGL_ARB_create_context") { - let mut attributes = Vec::new(); - - match opengl.version { - GlRequest::Latest => {} - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - GlRequest::Specific(Api::OpenGlEs, (major, minor)) => { - if extensions.split(' ').any(|i| i == "WGL_EXT_create_context_es2_profile") { - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as raw::c_int); - } else { - return Err(CreationError::OpenGlVersionNotSupported); - } - - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - GlRequest::Specific(_, _) => { - return Err(CreationError::OpenGlVersionNotSupported); - } - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int); - attributes.push(major as raw::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int); - attributes.push(minor as raw::c_int); - } - } - - if let Some(profile) = opengl.profile { - if extensions.split(' ').any(|i| i == "WGL_ARB_create_context_profile") { - let flag = match profile { - GlProfile::Compatibility => { - gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB - } - GlProfile::Core => gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, - }; - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int); - attributes.push(flag as raw::c_int); - } else { - return Err(CreationError::NotSupported( - "required extension \"WGL_ARB_create_context_profile\" not found" - .to_string(), - )); - } - } - - let flags = { - let mut flags = 0; - - // robustness - if extensions.split(' ').any(|i| i == "WGL_ARB_create_context_robustness") { - match opengl.robustness { - Robustness::RobustNoResetNotification - | Robustness::TryRobustNoResetNotification => { - attributes.push( - gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as raw::c_int); - flags |= gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::RobustLoseContextOnReset - | Robustness::TryRobustLoseContextOnReset => { - attributes.push( - gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB - as raw::c_int, - ); - attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as raw::c_int); - flags |= gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as raw::c_int; - } - Robustness::NotRobust => (), - Robustness::NoError => (), - } - } else { - match opengl.robustness { - Robustness::RobustNoResetNotification - | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - } - - if opengl.debug { - flags |= gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int; - } - - flags - }; - - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as raw::c_int); - attributes.push(flags); - - attributes.push(0); - - let ctx = extra_functions.CreateContextAttribsARB( - hdc as *const raw::c_void, - share as *const raw::c_void, - attributes.as_ptr(), - ); - - if ctx.is_null() { - return Err(CreationError::OsError(format!( - "wglCreateContextAttribsARB failed: {}", - std::io::Error::last_os_error() - ))); - } else { - return Ok(ContextWrapper(ctx as HGLRC)); - } - } - } else { - share = std::ptr::null_mut(); +unsafe fn load_extra_functions(win: HWND) -> Result<(&'static WglExtra, HashSet<&'static str>)> { + 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()); } + let rect = placement.rcNormalPosition; - let ctx = gl::wgl::CreateContext(hdc as *const raw::c_void); - if ctx.is_null() { - return Err(CreationError::OsError(format!( - "wglCreateContext failed: {}", - std::io::Error::last_os_error() - ))); + let mut class_name = [0u16; 128]; + if wm::GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { + return Err(IoError::last_os_error().into()); } - if !share.is_null() && gl::wgl::ShareLists(share as *const raw::c_void, ctx) == 0 { - return Err(CreationError::OsError(format!( - "wglShareLists failed: {}", - std::io::Error::last_os_error() - ))); - }; - - Ok(ContextWrapper(ctx as HGLRC)) -} - -/// Chooses a pixel formats without using WGL. -/// -/// Gives less precise results than `enumerate_arb_pixel_formats`. -unsafe fn choose_native_pixel_format_id( - hdc: HDC, - pf_reqs: &PixelFormatRequirements, -) -> Result { - // TODO: hardware acceleration is not handled - - // handling non-supported stuff - if pf_reqs.float_color_buffer { - return Err(()); + let instance = dll_loader::GetModuleHandleW(std::ptr::null()); + 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()); } - match pf_reqs.multisampling { - Some(0) => (), - None => (), - Some(_) => return Err(()), - }; + let class_name = + OsStr::new("WglDummy Window").encode_wide().chain(Some(0).into_iter()).collect::>(); - if pf_reqs.stereoscopy { - return Err(()); - } + class.cbSize = mem::size_of::() as _; + class.lpszClassName = class_name.as_ptr(); + class.lpfnWndProc = Some(wm::DefWindowProcW); - if pf_reqs.srgb { - return Err(()); - } + // 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). + wm::RegisterClassExW(&class); - if pf_reqs.release_behavior != ReleaseBehavior::Flush { - return Err(()); - } + // This dummy wnidow should match the real one enough to get the same OpenGL + // driver. + let title = + OsStr::new("dummy window").encode_wide().chain(Some(0).into_iter()).collect::>(); - // building the descriptor to pass to ChoosePixelFormat - let descriptor = PIXELFORMATDESCRIPTOR { - nSize: std::mem::size_of::() as u16, - nVersion: 1, - dwFlags: { - let f1 = match pf_reqs.double_buffer { - None => PFD_DOUBLEBUFFER, /* Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose */ - Some(true) => PFD_DOUBLEBUFFER, - Some(false) => 0, - }; + let ex_style = wm::WS_EX_APPWINDOW; + let style = wm::WS_POPUP | wm::WS_CLIPSIBLINGS | wm::WS_CLIPCHILDREN; + let win = 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(), + ); - let f2 = if pf_reqs.stereoscopy { PFD_STEREO } else { 0 }; - - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | f1 | f2 - }, - iPixelType: PFD_TYPE_RGBA, - cColorBits: pf_reqs.color_bits.unwrap_or(0), - cRedBits: 0, - cRedShift: 0, - cGreenBits: 0, - cGreenShift: 0, - cBlueBits: 0, - cBlueShift: 0, - cAlphaBits: pf_reqs.alpha_bits.unwrap_or(0), - cAlphaShift: 0, - cAccumBits: 0, - cAccumRedBits: 0, - cAccumGreenBits: 0, - cAccumBlueBits: 0, - cAccumAlphaBits: 0, - cDepthBits: pf_reqs.depth_bits.unwrap_or(0), - cStencilBits: pf_reqs.stencil_bits.unwrap_or(0), - cAuxBuffers: 0, - iLayerType: PFD_MAIN_PLANE, - bReserved: 0, - dwLayerMask: 0, - dwVisibleMask: 0, - dwDamageMask: 0, - }; - - // now querying - let pf_id = ChoosePixelFormat(hdc, &descriptor); - if pf_id == 0 { - return Err(()); + if win == 0 { + return Err(IoError::last_os_error().into()); } - Ok(pf_id) -} - -unsafe fn choose_native_pixel_format( - hdc: HDC, - pf_reqs: &PixelFormatRequirements, - pf_id: raw::c_int, -) -> Result { - // querying back the capabilities of what windows told us - let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed(); - if DescribePixelFormat( - hdc, - pf_id, - std::mem::size_of::() as u32, - &mut output, - ) == 0 - { - return Err(()); + let hdc = gdi::GetDC(win); + 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()); } - // windows may return us a non-conforming pixel format if none are - // supported, so we have to check this - if (output.dwFlags & PFD_DRAW_TO_WINDOW) == 0 { - return Err(()); - } - if (output.dwFlags & PFD_SUPPORT_OPENGL) == 0 { - return Err(()); - } - if output.iPixelType != PFD_TYPE_RGBA { - return Err(()); + let context = gl::wglCreateContext(hdc); + if gl::wglMakeCurrent(hdc, context) == 0 { + return Err(IoError::last_os_error().into()); } - let pf_desc = PixelFormat { - hardware_accelerated: (output.dwFlags & PFD_GENERIC_FORMAT) == 0, - color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits, - alpha_bits: output.cAlphaBits, - depth_bits: output.cDepthBits, - stencil_bits: output.cStencilBits, - stereoscopy: (output.dwFlags & PFD_STEREO) != 0, - double_buffer: (output.dwFlags & PFD_DOUBLEBUFFER) != 0, - multisampling: None, - srgb: false, - }; + // Load WGL. + let wgl_extra = WGL_EXTRA.get_or_init(WglExtra::new); + let client_extensions = display::load_extensions(hdc, wgl_extra); - if pf_desc.alpha_bits < pf_reqs.alpha_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.depth_bits < pf_reqs.depth_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.stencil_bits < pf_reqs.stencil_bits.unwrap_or(0) { - return Err(()); - } - if pf_desc.color_bits < pf_reqs.color_bits.unwrap_or(0) { - return Err(()); - } - if let Some(req) = pf_reqs.hardware_accelerated { - if pf_desc.hardware_accelerated != req { - return Err(()); - } - } - if let Some(req) = pf_reqs.double_buffer { - if pf_desc.double_buffer != req { - return Err(()); - } - } - - Ok(pf_desc) -} - -/// Enumerates the list of pixel formats by using extra WGL functions. -/// -/// Gives more precise results than `enumerate_native_pixel_formats`. -unsafe fn choose_arb_pixel_format_id( - extra: &gl::wgl_extra::Wgl, - extensions: &str, - hdc: HDC, - pf_reqs: &PixelFormatRequirements, -) -> Result { - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as raw::c_int); - out.push(1); - - out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as raw::c_int); - out.push(1); - - out.push(gl::wgl_extra::PIXEL_TYPE_ARB as raw::c_int); - if pf_reqs.float_color_buffer { - if extensions.split(' ').any(|i| i == "WGL_ARB_pixel_format_float") { - out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as raw::c_int); - } else { - return Err(()); - } - } else { - out.push(gl::wgl_extra::TYPE_RGBA_ARB as raw::c_int); - } - - if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated { - out.push(gl::wgl_extra::ACCELERATION_ARB as raw::c_int); - out.push(if hardware_accelerated { - gl::wgl_extra::FULL_ACCELERATION_ARB as raw::c_int - } else { - gl::wgl_extra::NO_ACCELERATION_ARB as raw::c_int - }); - } - - if let Some(color) = pf_reqs.color_bits { - out.push(gl::wgl_extra::COLOR_BITS_ARB as raw::c_int); - out.push(color as raw::c_int); - } - - if let Some(alpha) = pf_reqs.alpha_bits { - out.push(gl::wgl_extra::ALPHA_BITS_ARB as raw::c_int); - out.push(alpha as raw::c_int); - } - - if let Some(depth) = pf_reqs.depth_bits { - out.push(gl::wgl_extra::DEPTH_BITS_ARB as raw::c_int); - out.push(depth as raw::c_int); - } - - if let Some(stencil) = pf_reqs.stencil_bits { - out.push(gl::wgl_extra::STENCIL_BITS_ARB as raw::c_int); - out.push(stencil as raw::c_int); - } - - // Prefer double buffering if unspecified (probably shouldn't once you - // can choose) - let double_buffer = pf_reqs.double_buffer.unwrap_or(true); - out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as raw::c_int); - out.push(if double_buffer { 1 } else { 0 }); - - if let Some(multisampling) = pf_reqs.multisampling { - if extensions.split(' ').any(|i| i == "WGL_ARB_multisample") { - out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as raw::c_int); - out.push(if multisampling == 0 { 0 } else { 1 }); - out.push(gl::wgl_extra::SAMPLES_ARB as raw::c_int); - out.push(multisampling as raw::c_int); - } else { - return Err(()); - } - } - - out.push(gl::wgl_extra::STEREO_ARB as raw::c_int); - out.push(if pf_reqs.stereoscopy { 1 } else { 0 }); - - // WGL_*_FRAMEBUFFER_SRGB might be assumed to be true if not listed; - // so it's best to list it out and set its value as necessary. - if extensions.split(' ').any(|i| i == "WGL_ARB_framebuffer_sRGB") { - out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int); - out.push(pf_reqs.srgb as raw::c_int); - } else if extensions.split(' ').any(|i| i == "WGL_EXT_framebuffer_sRGB") { - out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int); - out.push(pf_reqs.srgb as raw::c_int); - } else if pf_reqs.srgb { - return Err(()); - } - - match pf_reqs.release_behavior { - ReleaseBehavior::Flush => (), - ReleaseBehavior::None => { - if extensions.split(' ').any(|i| i == "WGL_ARB_context_flush_control") { - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as raw::c_int); - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as raw::c_int); - } - } - } - - out.push(0); - out - }; - - let mut format_id = std::mem::zeroed(); - let mut num_formats = std::mem::zeroed(); - if extra.ChoosePixelFormatARB( - hdc as *const _, - descriptor.as_ptr(), - std::ptr::null(), - 1, - &mut format_id, - &mut num_formats, - ) == 0 - { - return Err(()); - } - - if num_formats == 0 { - return Err(()); - } - - Ok(format_id) -} - -unsafe fn choose_arb_pixel_format( - extra: &gl::wgl_extra::Wgl, - extensions: &str, - hdc: HDC, - format_id: raw::c_int, -) -> Result { - let get_info = |attrib: u32| { - let mut value = std::mem::zeroed(); - extra.GetPixelFormatAttribivARB( - hdc as *const _, - format_id as raw::c_int, - 0, - 1, - [attrib as raw::c_int].as_ptr(), - &mut value, - ); - value as u32 - }; - - let pf_desc = PixelFormat { - hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) - != gl::wgl_extra::NO_ACCELERATION_ARB, - color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 - + get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 - + get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8, - alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8, - depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8, - stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8, - stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0, - double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0, - multisampling: { - if extensions.split(' ').any(|i| i == "WGL_ARB_multisample") { - match get_info(gl::wgl_extra::SAMPLES_ARB) { - 0 => None, - a => Some(a as u16), - } - } else { - None - } - }, - srgb: if extensions.split(' ').any(|i| i == "WGL_ARB_framebuffer_sRGB") { - get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 - } else if extensions.split(' ').any(|i| i == "WGL_EXT_framebuffer_sRGB") { - get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 - } else { - false - }, - }; - - Ok(pf_desc) -} - -/// Calls `SetPixelFormat` on a window. -unsafe fn set_pixel_format(hdc: HDC, id: raw::c_int) -> Result<(), CreationError> { - let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed(); - - if DescribePixelFormat( - hdc, - id, - std::mem::size_of::() as UINT, - &mut output, - ) == 0 - { - return Err(CreationError::OsError(format!( - "DescribePixelFormat function failed: {}", - std::io::Error::last_os_error() - ))); - } + wm::DestroyWindow(win); + gl::wglDeleteContext(context); - if SetPixelFormat(hdc, id, &output) == 0 { - return Err(CreationError::OsError(format!( - "SetPixelFormat function failed: {}", - std::io::Error::last_os_error() - ))); - } - - Ok(()) + Ok((wgl_extra, client_extensions)) } -/// Loads the `opengl32.dll` library. -unsafe fn load_opengl32_dll() -> Result { - let name = - OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()).collect::>(); - - let lib = LoadLibraryW(name.as_ptr()); - - if lib.is_null() { - return Err(CreationError::OsError(format!( - "LoadLibrary function failed: {}", - std::io::Error::last_os_error() - ))); +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) } - - Ok(lib) -} - -/// Loads the WGL functions that are not guaranteed to be supported. -/// -/// The `window` must be passed because the driver can vary depending on the -/// window's characteristics. -unsafe fn load_extra_functions(win: HWND) -> Result { - let (ex_style, style) = (WS_EX_APPWINDOW, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); - - // creating a dummy invisible window - let dummy_win = { - // getting the rect of the real window - let rect = { - let mut placement: WINDOWPLACEMENT = std::mem::zeroed(); - placement.length = std::mem::size_of::() as UINT; - if GetWindowPlacement(win, &mut placement) == 0 { - panic!(); - } - placement.rcNormalPosition - }; - - // getting the class name of the real window - let mut class_name = [0u16; 128]; - if GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { - return Err(CreationError::OsError(format!( - "GetClassNameW function failed: {}", - std::io::Error::last_os_error() - ))); - } - - // access to class information of the real window - let instance = GetModuleHandleW(std::ptr::null()); - let mut class: WNDCLASSEXW = std::mem::zeroed(); - - if GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 { - return Err(CreationError::OsError(format!( - "GetClassInfoExW function failed: {}", - std::io::Error::last_os_error() - ))); - } - - // register a new class for the dummy window, - // similar to the class of the real window but with a different callback - let class_name = OsStr::new("WglDummy Class") - .encode_wide() - .chain(Some(0).into_iter()) - .collect::>(); - - class.cbSize = std::mem::size_of::() as UINT; - class.lpszClassName = class_name.as_ptr(); - class.lpfnWndProc = Some(DefWindowProcW); - - // this shouldn't fail if the registration of the real window class - // worked. multiple registrations of the window class trigger an - // error which we want to ignore silently (e.g for multi-window - // setups) - RegisterClassExW(&class); - - // this dummy window should match the real one enough to get the same - // OpenGL driver - let title = - OsStr::new("dummy window").encode_wide().chain(Some(0).into_iter()).collect::>(); - let win = CreateWindowExW( - ex_style, - class_name.as_ptr(), - title.as_ptr() as LPCWSTR, - style, - CW_USEDEFAULT, - CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - std::ptr::null_mut(), - std::ptr::null_mut(), - GetModuleHandleW(std::ptr::null()), - std::ptr::null_mut(), - ); - - if win.is_null() { - return Err(CreationError::OsError(format!( - "CreateWindowEx function failed: {}", - std::io::Error::last_os_error() - ))); - } - - let hdc = GetDC(win); - if hdc.is_null() { - let err = Err(CreationError::OsError(format!( - "GetDC function failed: {}", - std::io::Error::last_os_error() - ))); - return err; - } - - WindowWrapper(win, hdc) - }; - - // getting the pixel format that we will use and setting it - { - let id = choose_dummy_pixel_format(dummy_win.1)?; - set_pixel_format(dummy_win.1, id)?; - } - - // creating the dummy OpenGL context and making it current - let dummy_ctx = create_context(None, dummy_win.0, dummy_win.1)?; - let _current_context = CurrentContextGuard::make_current(dummy_win.1, dummy_ctx.0)?; - - // loading the extra WGL functions - Ok(gl::wgl_extra::Wgl::load_with(|addr| { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - gl::wgl::GetProcAddress(addr) as *const raw::c_void - })) -} - -/// This function chooses a pixel format that is likely to be provided by -/// the main video driver of the system. -fn choose_dummy_pixel_format(hdc: HDC) -> Result { - // building the descriptor to pass to ChoosePixelFormat - let descriptor = PIXELFORMATDESCRIPTOR { - nSize: std::mem::size_of::() as u16, - nVersion: 1, - dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - iPixelType: PFD_TYPE_RGBA, - cColorBits: 24, - cRedBits: 0, - cRedShift: 0, - cGreenBits: 0, - cGreenShift: 0, - cBlueBits: 0, - cBlueShift: 0, - cAlphaBits: 8, - cAlphaShift: 0, - cAccumBits: 0, - cAccumRedBits: 0, - cAccumGreenBits: 0, - cAccumBlueBits: 0, - cAccumAlphaBits: 0, - cDepthBits: 24, - cStencilBits: 8, - cAuxBuffers: 0, - iLayerType: PFD_MAIN_PLANE, - bReserved: 0, - dwLayerMask: 0, - dwVisibleMask: 0, - dwDamageMask: 0, - }; - - // now querying - let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) }; - if pf_id == 0 { - return Err(CreationError::OsError("No available pixel format".to_owned())); - } - - Ok(pf_id) } diff --git a/glutin/src/api/wgl/surface.rs b/glutin/src/api/wgl/surface.rs new file mode 100644 index 0000000000..a8c3dc8670 --- /dev/null +++ b/glutin/src/api/wgl/surface.rs @@ -0,0 +1,184 @@ +//! A wrapper around `HWND` used for GL operations. + +use std::fmt; +use std::io::Error as IoError; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +use raw_window_handle::RawWindowHandle; +use windows_sys::Win32::Foundation::HWND; +use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; + +use crate::config::GetGlConfig; +use crate::display::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) fn create_pbuffer_surface( + &self, + _config: &Config, + _surface_attributes: &SurfaceAttributes, + ) -> Result> { + Err(ErrorKind::NotSupported("pbuffers are not implemented with WGL").into()) + } + + 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 _ = config.apply_on_native_window(handle); + window_handle.hwnd as HWND + }, + _ => { + return Err( + ErrorKind::NotSupported("provided native window is not supported").into() + ) + }, + }; + + let surface = + Surface { display: self.clone(), config: config.clone(), hwnd, _ty: PhantomData }; + + Ok(surface) + } +} + +/// A Wrapper around `HWND`. +pub struct Surface { + display: Display, + config: Config, + pub(crate) hwnd: HWND, + _ty: PhantomData, +} + +impl Drop for Surface { + fn drop(&mut self) { + // This line intentionally left blank. + } +} + +impl GlSurface for Surface { + type Context = PossiblyCurrentContext; + type SurfaceType = T; + + fn buffer_age(&self) -> u32 { + 0 + } + + fn width(&self) -> Option { + None + } + + fn height(&self) -> Option { + None + } + + fn is_single_buffered(&self) -> bool { + self.config.is_single_buffered() + } + + fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { + unsafe { + let hdc = gdi::GetDC(self.hwnd); + if gl::SwapBuffers(hdc) == 0 { + Err(IoError::last_os_error().into()) + } else { + Ok(()) + } + } + } + + fn set_swap_interval(&self, _context: &Self::Context, interval: SwapInterval) -> Result<()> { + let interval = match interval { + SwapInterval::DontWait => 0, + SwapInterval::Wait(n) => n.get(), + }; + + let res = match self.display.inner.wgl_extra { + Some(extra) + if self.display.inner.client_extensions.contains("WGL_EXT_swap_control") => + unsafe { extra.SwapIntervalEXT(interval as _) }, + _ => { + return Err( + ErrorKind::NotSupported("swap contol extrensions are not supported").into() + ) + }, + }; + + if res == 0 { + Err(IoError::last_os_error().into()) + } else { + Ok(()) + } + } + + 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("hwnd", &self.hwnd) + .finish() + } +} + +impl AsRawSurface for Surface { + fn raw_surface(&self) -> RawSurface { + RawSurface::Wgl(self.hwnd 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 {} diff --git a/glutin/src/config.rs b/glutin/src/config.rs new file mode 100644 index 0000000000..1bfa0ed531 --- /dev/null +++ b/glutin/src/config.rs @@ -0,0 +1,484 @@ +//! GL 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. + fn color_buffer_type(&self) -> ColorBufferType; + + /// 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 sample buffers used for multisampling. + fn sample_buffers(&self) -> u8; + + /// Whether the config supports creating srgb capable + /// [`crate::surface::Surface`]. + fn srgb_capable(&self) -> bool; + + /// The type of the surfaces that can be created with this config. + fn config_surface_types(&self) -> ConfigSurfaceTypes; + + /// The [`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)] +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 + } + + /// Wether the floating pixel formats should be used. + /// + /// By default `false` is requested. + #[inline] + pub fn with_float_pixels(mut self, float_pixels: bool) -> Self { + self.template.float_pixels = float_pixels; + self + } + + /// Number of bits in the stencil buffer. + /// + /// By default `0` is requested. + #[inline] + pub fn with_stencil_size(mut self, stencil_size: u8) -> Self { + self.template.stencil_size = stencil_size; + self + } + + /// Number of bits in the depth buffer. + /// + /// By default `0` is requested. + #[inline] + pub fn with_depth_size(mut self, depth_size: u8) -> Self { + self.template.depth_size = depth_size; + self + } + + /// Number of sample buffers. + /// + /// By default `0` is requested. + #[inline] + pub fn with_sample_buffers(mut self, sample_buffers: u8) -> Self { + self.template.sample_buffers = sample_buffers; + self + } + + /// The types of the surfaces that must be supported by the configuration. + /// + /// By default only the `WINDOW` bit is set. + #[inline] + pub fn with_surface_type(mut self, config_surface_types: ConfigSurfaceTypes) -> Self { + self.template.config_surface_types = config_surface_types; + self + } + + /// The type of the color buffer. + /// + /// By default `RGB` buffer with all components sizes of `8` is requested. + #[inline] + pub fn with_buffer_type(mut self, color_buffer_type: ColorBufferType) -> Self { + self.template.color_buffer_type = color_buffer_type; + self + } + + /// The set of apis that are supported by configuration. + /// + /// By default `OpenGL` api is used. + #[inline] + pub fn with_api(mut self, api: Api) -> Self { + self.template.api = api; + self + } + + /// Wether the stereo pairs should be present. + /// + /// By default it isn't specified. + #[inline] + pub fn with_stereoscopy(mut self, stereoscopy: Option) -> Self { + self.template.stereoscopy = stereoscopy; + self + } + + /// Wether the single buffer should be used. + /// + /// By default `false` is requested. + #[inline] + pub fn with_single_buffering(mut self, single_buffering: bool) -> Self { + self.template.single_buffering = single_buffering; + self + } + + /// Wether the configuration should support transparency. + /// + /// The default is `false`. + /// + /// # Api-specific + /// + /// EGL on X11 doesn't provide a way to create a transparent surface. Use + /// GLX for that instead. + #[inline] + pub fn with_transparency(mut self, transparency: bool) -> Self { + self.template.transparency = transparency; + self + } + + /// With the maximum sizes of pbuffer. + #[inline] + pub fn with_pbuffer_sizes(mut self, width: NonZeroU32, height: NonZeroU32) -> Self { + self.template.max_pbuffer_width = Some(width.into()); + self.template.max_pbuffer_height = Some(height.into()); + self + } + + /// 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 sample buffers. + pub(crate) sample_buffers: u8, + + /// The minimum swap interval supported by the configuration. + pub(crate) min_swap_interval: Option, + + /// The maximum swap interval supported by the configuration. + pub(crate) max_swap_interval: Option, + + /// The types of the surfaces supported by the configuration. + pub(crate) config_surface_types: ConfigSurfaceTypes, + + /// The rendering Api's supported by the configuration. + pub(crate) api: Api, + + /// 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 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, + + sample_buffers: 0, + + transparency: true, + + stereoscopy: None, + + min_swap_interval: None, + + max_swap_interval: None, + + single_buffering: false, + + float_pixels: false, + + config_surface_types: ConfigSurfaceTypes::WINDOW, + + max_pbuffer_width: None, + max_pbuffer_height: None, + + native_window: None, + + api: Api::OPENGL, + } + } +} + +bitflags! { + /// The types of the surface supported by the config. + 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. + 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) -> ColorBufferType { + 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 sample_buffers(&self) -> u8 { + gl_api_dispatch!(self; Self(config) => config.sample_buffers()) + } + + 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 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()) + } +} diff --git a/glutin/src/context.rs b/glutin/src/context.rs index 715df05e22..46c07c515a 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -1,168 +1,584 @@ -use super::*; +//! OpenGL context handling. -use std::marker::PhantomData; -use winit::event_loop::EventLoopWindowTarget; +#![allow(unreachable_patterns)] +use std::ffi::{self, CStr}; -/// Represents an OpenGL [`Context`]. -/// -/// A [`Context`] is normally associated with a single Window, however -/// [`Context`]s can be *shared* between multiple windows or be headless. -/// -/// If a [`Context`] is backed by a window, it will be wrapped by either -/// [`RawContext`] or [`WindowedContext`]. +use raw_window_handle::RawWindowHandle; + +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 not current operations. +pub trait NotCurrentGlContext: Sealed { + /// The type of possibly current context. + type PossiblyCurrentContext: PossiblyCurrentGlContext; + + /// 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_current(self) -> Self::PossiblyCurrentContext; +} + +/// A trait that splits the methods accessing [`crate::surface::Surface`] on not +/// current context. +pub trait NotCurrentGlContextSurfaceAccessor: Sealed { + /// The surface supported by the context. + type Surface: GlSurface; + /// The possibly current context produced when making not current context + /// current. + type PossiblyCurrentContext: PossiblyCurrentGlContext; + + /// Make [`Self::Surface`] on the calling thread producing the + /// [`Self::PossiblyCurrentContext`] indicating that the context could + /// be current on the theard. + 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; + + /// 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. + fn make_not_current(self) -> Result; + + /// Returns the address of an OpenGL function. The context must be current + /// when doing so. + fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void; +} + +/// A trait that splits the methods accessing [`crate::surface::Surface`]. +pub trait PossiblyCurrentContextGlSurfaceAccessor: Sealed { + /// The surface supported by the context. + type Surface: GlSurface; + + /// Make [`Self::Surface`] current on the calling thread. + 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: + /// + /// This is not supported with WGL and CGL. + 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)] +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. + /// + /// 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 + /// + /// On Wayland both context 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 [`NotRobust`], because this is what typically expected + /// when you create an OpenGL context. However for safety you should + /// consider [`RobustLoseContextOnReset`]. + /// + /// [`Robustness`]: crate::context::Robustness + /// [`NotRobust`]: crate::context::Robustness::NotRobust + /// [`RobustLoseContextOnReset`]: crate::context::Robustness::RobustLoseContextOnReset + pub fn with_robustness(mut self, robustness: Robustness) -> Self { + self.attributes.robustness = robustness; + self + } + + /// The behaviour when changing the current context. See the docs of + /// [`ReleaseBehaviour`]. + /// + /// The default is [`Flush`]. + /// + /// [`ReleaseBehaviour`]: crate::context::ReleaseBehaviour + /// [`Flush`]: crate::context::ReleaseBehaviour::Flush + pub fn with_release_behavior(mut self, release_behavior: ReleaseBehaviour) -> 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. + /// + /// [`GlProfile`]: crate::context::GlProfile + 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 OpenGL is requested. + /// + /// [`ContextApi`]: crate::context::ContextApi + pub fn with_context_api(mut self, api: ContextApi) -> Self { + self.attributes.api = api; + self + } + + /// Build the context attributes. + /// + /// The `raw_window_handle` isn't required and here for WGL compatibility. + /// + /// #Api-specific + /// + /// **WGL:** - you must pass `raw_window_handle` for if you plan to use it + /// with 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)] +pub struct ContextAttributes { + pub(crate) release_behavior: ReleaseBehaviour, + + pub(crate) debug: bool, + + pub(crate) robustness: Robustness, + + pub(crate) profile: Option, + + pub(crate) api: ContextApi, + + 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)] +pub enum Robustness { + /// Not everything is checked. Your application can crash if you do + /// something wrong with your shaders. + NotRobust, + + /// The driver doesn't check anything. This option is very dangerous. + /// Please know what you're doing before using it. See the + /// `GL_KHR_no_error` extension. + /// + /// Since this option is purely an optimization, no error will be returned + /// if the backend doesn't support it. Instead it will automatically + /// fall back to [`NotRobust`]. + /// + /// [`NotRobust`]: crate::context::Robustness::NotRobust + NoError, + + /// Everything is checked to avoid any crash. The driver will attempt to + /// avoid any problem, but if a problem occurs the behavior is + /// implementation-defined. You are just guaranteed not to get a crash. + RobustNoResetNotification, + + /// Everything is checked to avoid any crash. If a problem occurs, the + /// context will enter a "context lost" state. It must then be + /// recreated. + RobustLoseContextOnReset, +} + +impl Default for Robustness { + #[inline] + fn default() -> Self { + Robustness::NotRobust + } +} + +/// Describes the requested OpenGL context profiles. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GlProfile { + /// Include all the future-compatible functions and definitions. + 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 a `ApiVersion` any OpenGl context will be picked. + OpenGl(Option), + + /// OpenGL Api version that should be used by the context. + /// + /// When using `None` as a `ApiVersion` any Gles context will be picked. + Gles(Option), +} + +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 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)] +pub enum ReleaseBehaviour { + /// Doesn't do anything. Most notably doesn't flush. Not supported by all + /// drivers. + /// + /// # Api-specific + /// + /// **macOS:** no supported, [`Self::Flush`] is always used. + None, + + /// Flushes the context that was previously current as if `glFlush` was + /// called. This is the default behaviour. + Flush, +} + +impl Default for ReleaseBehaviour { + #[inline] + fn default() -> Self { + ReleaseBehaviour::Flush + } +} + +/// A context that is known to be not current on the current thread. /// -/// # Example +/// 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 main() { -/// # let el = glutin::event_loop::EventLoop::new(); -/// # let wb = glutin::window::WindowBuilder::new(); -/// # let some_context = glutin::ContextBuilder::new() -/// # .build_windowed(wb, &el) -/// # .unwrap(); -/// let cb = glutin::ContextBuilder::new() -/// .with_vsync(true) -/// .with_multisampling(8) -/// .with_shared_lists(some_context.context()); -/// # } +/// fn test_send() {} +/// test_send::(); +/// ``` +/// However it's not `Sync`. +/// ```compile_fail +/// fn test_sync() {} +/// test_sync::(); /// ``` #[derive(Debug)] -pub struct Context { - pub(crate) context: platform_impl::Context, - pub(crate) phantom: PhantomData, +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 Context { - /// See [`ContextWrapper::make_current()`]. - pub unsafe fn make_current(self) -> Result, (Self, ContextError)> { - match self.context.make_current() { - Ok(()) => Ok(Context { context: self.context, phantom: PhantomData }), - Err(err) => Err((Context { context: self.context, phantom: PhantomData }, err)), - } +impl NotCurrentGlContext for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + + fn treat_as_current(self) -> Self::PossiblyCurrentContext { + gl_api_dispatch!(self; Self(context) => context.treat_as_current(); as PossiblyCurrentContext) } +} + +impl NotCurrentGlContextSurfaceAccessor for NotCurrentContext { + type PossiblyCurrentContext = PossiblyCurrentContext; + type Surface = Surface; - /// See [`ContextWrapper::make_not_current()`]. - pub unsafe fn make_not_current(self) -> Result, (Self, ContextError)> { - match self.context.make_not_current() { - Ok(()) => Ok(Context { context: self.context, phantom: PhantomData }), - Err(err) => Err((Context { context: self.context, phantom: PhantomData }, err)), + 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!(), } } - /// See [`ContextWrapper::treat_as_not_current()`]. - pub unsafe fn treat_as_not_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } - } - - /// See [`ContextWrapper::treat_as_current()`]. - pub unsafe fn treat_as_current(self) -> Context { - Context { context: self.context, phantom: PhantomData } - } - - /// See [`ContextWrapper::is_current()`]. - pub fn is_current(&self) -> bool { - self.context.is_current() - } - - /// See [`ContextWrapper::get_api()`]. - pub fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl Context { - /// See [`ContextWrapper::get_proc_address()`]. - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.context.get_proc_address(addr) - } -} - -#[allow(rustdoc::broken_intra_doc_links)] -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Builds the given GL context. - /// - /// When on a unix operating system, prefer [`build_surfaceless()`]. If both - /// [`build_surfaceless()`] and [`build_headless()`][Self::build_headless()] - /// fail, try using a hidden window, or [`build_osmesa()`]. Please note that - /// if you choose to use a hidden window, you must still handle the events - /// it generates on the events loop. - /// - /// Errors can occur in two scenarios: - /// - If the window could not be created (via permission denied, - /// incompatible system, out of memory, etc.). This should be very rare. - /// - If the OpenGL [`Context`] could not be created. This generally - /// happens - /// because the underlying platform doesn't support a requested feature. - #[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\n - [`build_surfaceless()`]: crate::platform\n - [`build_osmesa()`]: crate::platform - " - )] - #[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\n - [`build_surfaceless()`]: platform::unix::HeadlessContextExt::build_surfaceless()\n - [`build_osmesa()`]: platform::unix::HeadlessContextExt::build_osmesa() - " - )] - pub fn build_headless( + fn make_current_draw_read( self, - el: &EventLoopWindowTarget, - size: dpi::PhysicalSize, - ) -> Result, CreationError> { - let ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - platform_impl::Context::new_headless(el, &pf_reqs, &gl_attr, size) - .map(|context| Context { context, phantom: PhantomData }) + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result { + match (self, surface_draw, surface_read) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { + Ok(PossiblyCurrentContext::Egl(context.make_current_draw_read(draw, read)?)) + }, + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { + Ok(PossiblyCurrentContext::Glx(context.make_current_draw_read(draw, read)?)) + }, + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { + Ok(PossiblyCurrentContext::Wgl(context.make_current_draw_read(draw, read)?)) + }, + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { + Ok(PossiblyCurrentContext::Cgl(context.make_current_draw_read(draw, read)?)) + }, + _ => unreachable!(), + } } } -// This is nightly only: -// impl !Send for Context {} -// impl !Sync for Context {} -// -// Instead we add a phantom type to PossiblyCurrent +impl GetGlDisplay for NotCurrentContext { + type Target = Display; -/// A type that [`Context`]s which might possibly be currently current on some -/// thread take as a generic. -/// -/// See [`ContextWrapper::make_current()`] for more details. -#[derive(Debug, Clone, Copy)] -pub struct PossiblyCurrent { - phantom: PhantomData<*mut ()>, + fn display(&self) -> Self::Target { + gl_api_dispatch!(self; Self(context) => context.display(); as Display) + } } -/// A type that [`Context`]s which are not currently current on any thread take -/// as a generic. +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`]. +/// ```no_run +/// fn test_send() {} +/// test_send::(); +/// ``` +/// +/// However it's not `Sync`. /// -/// See [`ContextWrapper::make_current()`] for more details. -#[derive(Debug, Clone, Copy)] -pub enum NotCurrent {} +/// ```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; + + 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 get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { + gl_api_dispatch!(self; Self(context) => context.get_proc_address(addr)) + } +} + +impl PossiblyCurrentContextGlSurfaceAccessor for PossiblyCurrentContext { + type Surface = Surface; + + fn make_current(&self, surface: &Self::Surface) -> Result<()> { + match (self, surface) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface), + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface), + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(surface)) => context.make_current(surface), + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(surface)) => context.make_current(surface), + _ => unreachable!(), + } + } + + fn make_current_draw_read( + &self, + surface_draw: &Self::Surface, + surface_read: &Self::Surface, + ) -> Result<()> { + match (self, surface_draw, surface_read) { + #[cfg(egl_backend)] + (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => { + context.make_current_draw_read(draw, read) + }, + #[cfg(glx_backend)] + (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => { + context.make_current_draw_read(draw, read) + }, + #[cfg(wgl_backend)] + (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => { + context.make_current_draw_read(draw, read) + }, + #[cfg(cgl_backend)] + (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => { + context.make_current_draw_read(draw, read) + }, + _ => unreachable!(), + } + } +} + +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), -/// A trait implemented on both [`NotCurrent`] and -/// [`PossiblyCurrent`]. -pub trait ContextCurrentState: std::fmt::Debug + Clone {} + /// Raw GLX context. + #[cfg(glx_backend)] + Glx(*const ffi::c_void), -impl ContextCurrentState for PossiblyCurrent {} -impl ContextCurrentState for NotCurrent {} + /// HGLRC pointer. + #[cfg(wgl_backend)] + Wgl(*const ffi::c_void), -trait FailToCompileIfNotSendSync -where - Self: Send + Sync, -{ + /// Pointer to NSOpenGLContext. + #[cfg(cgl_backend)] + Cgl(*const ffi::c_void), } -impl FailToCompileIfNotSendSync for Context {} diff --git a/glutin/src/display.rs b/glutin/src/display.rs new file mode 100644 index 0000000000..765df9c15c --- /dev/null +++ b/glutin/src/display.rs @@ -0,0 +1,502 @@ +//! The GL platform display creation and picking. +#![allow(unreachable_patterns)] + +use std::fmt; + +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 configuration matching the given `template`. + /// + /// # Safety + /// + /// Some platforms use [`RawWindowHandle`] to perform config picking, 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. + /// + /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle + 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 mark 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; +} + +/// 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; +} + +/// 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 could 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. + /// + /// # Safety + /// + /// The `display` must point to the valid platform display. + pub unsafe fn from_raw(display: RawDisplayHandle, picker: DisplayPicker) -> Result { + #[cfg(glx_backend)] + let registrar = picker + .glx_error_registrar + .expect("glx was requested, but error hook registrar wasn't provided."); + + match picker.api_preference { + #[cfg(egl_backend)] + DisplayApiPreference::Egl => Ok(Self::Egl(EglDisplay::from_raw(display)?)), + #[cfg(glx_backend)] + DisplayApiPreference::Glx => Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)), + #[cfg(wgl_backend)] + DisplayApiPreference::Wgl => { + Ok(Self::Wgl(WglDisplay::from_raw(display, picker.window_handle)?)) + }, + #[cfg(cgl_backend)] + DisplayApiPreference::Cgl => Ok(Self::Cgl(CglDisplay::from_raw(display)?)), + + #[cfg(all(egl_backend, glx_backend))] + DisplayApiPreference::EglThenGlx => { + if let Ok(display) = EglDisplay::from_raw(display) { + Ok(Self::Egl(display)) + } else { + Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)) + } + }, + #[cfg(all(egl_backend, glx_backend))] + DisplayApiPreference::GlxThenEgl => { + if let Ok(display) = GlxDisplay::from_raw(display, registrar) { + Ok(Self::Glx(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } + }, + + #[cfg(all(egl_backend, wgl_backend))] + DisplayApiPreference::EglThenWgl => { + if let Ok(display) = EglDisplay::from_raw(display) { + Ok(Self::Egl(display)) + } else { + Ok(Self::Wgl(WglDisplay::from_raw(display, picker.window_handle)?)) + } + }, + #[cfg(all(egl_backend, wgl_backend))] + DisplayApiPreference::WglThenEgl => { + if let Ok(display) = WglDisplay::from_raw(display, picker.window_handle) { + Ok(Self::Wgl(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(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) => { + Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Egl))) + }, + #[cfg(glx_backend)] + Self::Glx(display) => { + Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Glx))) + }, + #[cfg(wgl_backend)] + Self::Wgl(display) => { + Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Wgl))) + }, + #[cfg(cgl_backend)] + Self::Cgl(display) => { + Ok(Box::new(display.find_configs(template)?.into_iter().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)) => { + Ok(NotCurrentContext::Egl(display.create_context(config, context_attributes)?)) + }, + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(NotCurrentContext::Glx(display.create_context(config, context_attributes)?)) + }, + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(NotCurrentContext::Wgl(display.create_context(config, context_attributes)?)) + }, + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(NotCurrentContext::Cgl(display.create_context(config, context_attributes)?)) + }, + _ => unreachable!(), + } + } + + unsafe fn create_window_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?)) + }, + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_window_surface(config, surface_attributes)?)) + }, + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_window_surface(config, surface_attributes)?)) + }, + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_window_surface(config, surface_attributes)?)) + }, + _ => unreachable!(), + } + } + + 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)) => { + Ok(Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)?)) + }, + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pbuffer_surface(config, surface_attributes)?)) + }, + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_pbuffer_surface(config, surface_attributes)?)) + }, + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_pbuffer_surface(config, surface_attributes)?)) + }, + _ => unreachable!(), + } + } + + unsafe fn create_pixmap_surface( + &self, + config: &Self::Config, + surface_attributes: &SurfaceAttributes, + ) -> Result { + match (self, config) { + #[cfg(egl_backend)] + (Self::Egl(display), Config::Egl(config)) => { + Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?)) + }, + #[cfg(glx_backend)] + (Self::Glx(display), Config::Glx(config)) => { + Ok(Surface::Glx(display.create_pixmap_surface(config, surface_attributes)?)) + }, + #[cfg(wgl_backend)] + (Self::Wgl(display), Config::Wgl(config)) => { + Ok(Surface::Wgl(display.create_pixmap_surface(config, surface_attributes)?)) + }, + #[cfg(cgl_backend)] + (Self::Cgl(display), Config::Cgl(config)) => { + Ok(Surface::Cgl(display.create_pixmap_surface(config, surface_attributes)?)) + }, + _ => unreachable!(), + } + } +} + +impl AsRawDisplay for Display { + fn raw_display(&self) -> RawDisplay { + gl_api_dispatch!(self; Self(display) => display.raw_display()) + } +} + +impl Sealed for Display {} + +/// A settings to control automatic display picking. +pub struct DisplayPicker { + pub(crate) api_preference: DisplayApiPreference, + #[cfg(glx_backend)] + pub(crate) glx_error_registrar: Option, + #[cfg(wgl_backend)] + pub(crate) window_handle: Option, +} + +impl DisplayPicker { + /// Create a default display picker. + pub fn new() -> Self { + Default::default() + } + + /// The preference of the underlying system Api. + /// + /// For platforms supporting `EGL` the default is `EGL` given that other api + /// providers require extra options to get them work. + pub fn with_api_preference(mut self, api_preference: DisplayApiPreference) -> Self { + self.api_preference = api_preference; + self + } + + /// Create WGL display which will be the most suitable to operate with the + /// given window. + /// + /// When the window isn't provided the support for extensions and pixel + /// formats may be lacking. + #[cfg(wgl_backend)] + pub fn with_most_compatible_for_window( + mut self, + window_handle: raw_window_handle::RawWindowHandle, + ) -> Self { + self.window_handle = Some(window_handle); + self + } + + /// The hook to register glutin error handler in X11 error handling + /// function. + /// + /// The hook registrar must be provided in case GLX will be used. + #[cfg(glx_backend)] + pub fn with_glx_error_registrar(mut self, error_registrar: XlibErrorHookRegistrar) -> Self { + self.glx_error_registrar = Some(error_registrar); + self + } +} + +impl Default for DisplayPicker { + #[cfg(all(egl_backend, glx_backend))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl, glx_error_registrar: None } + } + + #[cfg(all(egl_backend, not(glx_backend), not(wgl_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Egl } + } + + #[cfg(all(glx_backend, not(egl_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Glx, glx_error_registrar: None } + } + + #[cfg(all(wgl_backend, not(egl_backend)))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Wgl, window_handle: None } + } + + #[cfg(all(wgl_backend, egl_backend))] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::WglThenEgl, window_handle: None } + } + + #[cfg(cgl_backend)] + fn default() -> Self { + Self { api_preference: DisplayApiPreference::Cgl } + } +} + +impl fmt::Debug for DisplayPicker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("DisplayPicker"); + let f = f.field("api_preference", &self.api_preference); + + #[cfg(glx_backend)] + let f = f.field("glx_registrar", &self.glx_error_registrar.is_some()); + + #[cfg(wgl_backend)] + let f = f.field("window_handle", &self.window_handle); + + f.finish() + } +} + +/// Preference of the display that should be used. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DisplayApiPreference { + /// Prefer EGL. + #[cfg(egl_backend)] + Egl, + /// Prefer GLX. + #[cfg(glx_backend)] + Glx, + /// Prefer WGL. + #[cfg(wgl_backend)] + Wgl, + /// Prefer CGL. + #[cfg(cgl_backend)] + Cgl, + + /// Prefer EGL and fallback to GLX. + #[cfg(all(egl_backend, glx_backend))] + EglThenGlx, + /// Prefer GLX and fallback to EGL. + #[cfg(all(egl_backend, glx_backend))] + GlxThenEgl, + + /// Prefer EGL and fallback to GLX. + #[cfg(all(egl_backend, wgl_backend))] + EglThenWgl, + /// Prefer GLX and fallback to EGL. + #[cfg(all(egl_backend, wgl_backend))] + WglThenEgl, +} + +/// 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, +} diff --git a/glutin/src/error.rs b/glutin/src/error.rs new file mode 100644 index 0000000000..fc13dc57ac --- /dev/null +++ b/glutin/src/error.rs @@ -0,0 +1,177 @@ +//! The catch all error used by glutin. + +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 mathing. + 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 if 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, "[{:x}] ", raw_code)?; + } + + let msg = if let Some(raw_os_message) = self.raw_os_message.as_ref() { + raw_os_message + } else { + self.kind.as_str() + }; + + write!(f, "{}", msg) + } +} + +impl std::error::Error for Error {} + +/// Build an error with just a kind. +impl From for Error { + fn from(kind: ErrorKind) -> Self { + Error { raw_code: None, raw_os_message: None, kind } + } +} + +/// A list specifying general categoires of native platform graphics interface +/// errors. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum ErrorKind { + /// The requested resource wasn't found. + NotFound, + + /// Failed to perform resource initialization. + InitializationFailed, + + /// Can't access a requested resource. + /// + /// For example when trying to make a context current while it's current on + /// another thread. + BadAccess, + + /// An operation could not be completed, because it failed to allocate + /// enough memory. + OutOfMemory, + + /// Anrecognized attribute value was passed. + BadAttribute, + + /// The context is no longer valid. + BadContext, + + /// The context is in bad state. + BadContextState, + + /// Invalid config was passed. + BadConfig, + + /// The current surface of the calling thread is no longer valid. + BadCurrentSurface, + + /// The display is no longer valid. + BadDisplay, + + /// The surface is invalid. + BadSurface, + + /// The pbuffer is invalid. + BadPbuffer, + + /// The pixmap is invalid. + BadPixmap, + + /// Arguments are inconsistent. For example when sharad context are not + /// compatible. + BadMatch, + + /// One or more argument values are invalid. + BadParameter, + + /// Bad native pixmap was provided. + BadNativePixmap, + + /// Bad native window was provided. + BadNativeWindow, + + /// The context was lost. + ContextLost, + + /// The operation is not supported by the platform. + NotSupported(&'static str), + + /// The misc error that can't be classyfied occured. + Misc, +} + +impl ErrorKind { + pub(crate) fn as_str(&self) -> &'static str { + use ErrorKind::*; + match *self { + NotFound => "not found", + InitializationFailed => "initialization failed", + BadAccess => "access to the resource failed", + OutOfMemory => "out of memory", + BadAttribute => "an anrecougnized attribute or attribute value was passed", + BadContext => "argument does not name a valid context", + BadContextState => "the context is in a bad state", + BadConfig => "argument does not name a valid config", + BadCurrentSurface => "the current surface of the calling thread is no longer valid", + BadDisplay => "argument does not name a valid display", + BadSurface => "argument does not name a valid surface", + BadPbuffer => "argument does not name a valid pbuffer", + BadPixmap => "argument does not name a valid pixmap", + BadMatch => "arguments are inconsistance", + BadParameter => "one or more argument values are invalid", + BadNativePixmap => "argument does not refer to a valid native pixmap", + BadNativeWindow => "argument does not refer to a valid native window", + ContextLost => "context loss", + NotSupported(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()) + } +} diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs index 74d46b1f6e..de7ff74400 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -1,664 +1,91 @@ -//! The purpose of this library is to provide an OpenGL [`Context`] on as many -//! platforms as possible. +//! The purpose of this library is to provide OpenGL `[context]` on as many +//! platforms as possible abstracting away the underlying platforms shenanigans, +//! while keeping direct access to them to make the use of platform specific +//! extensions easier. //! -//! # Building a [`WindowedContext`] +//! However the library doesn't force into using a cross platform abstraction, +//! for example, when only `[EGL]` is desired, it can be used directly. //! -//! A [`WindowedContext`] is composed of a [`Window`] and an OpenGL -//! [`Context`]. +//! The initialization starts by loading and connecting to the underlying +//! graphics platform Api when creating a `[display]`. This object is used to +//! create all the OpenGL objects, such as `[config]`, `[context]`, and +//! `[surface]`. //! -//! Due to some operating-system-specific quirks, glutin prefers control over -//! the order of creation of the [`Context`] and [`Window`]. Here is an example -//! of building a [`WindowedContext`]: -//! -//! ```no_run -//! # fn main() { -//! let el = glutin::event_loop::EventLoop::new(); -//! let wb = glutin::window::WindowBuilder::new() -//! .with_title("Hello world!") -//! .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); -//! let windowed_context = glutin::ContextBuilder::new() -//! .build_windowed(wb, &el) -//! .unwrap(); -//! # } -//! ``` -//! -//! You can, of course, create a [`RawContext`] separately from an existing -//! window, however that may result in an suboptimal configuration of the window -//! on some platforms. In that case use the unsafe platform-specific -//! [`RawContextExt`] available on unix operating systems and Windows. -//! -//! You can also produce headless [`Context`]s via the -//! [`ContextBuilder::build_headless()`] function. -//! -//! [`Window`]: crate::window::Window -#![cfg_attr( - target_os = "windows", - doc = "\ -[`RawContextExt`]: crate::platform::windows::RawContextExt -" -)] -#![cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "windows", - target_os = "openbsd", - )), - doc = "\ -[`RawContextExt`]: crate::platform -" -)] -#![cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`RawContextExt`]: crate::platform::unix::RawContextExt -" -)] +//! [display]: crate::display +//! [context]: crate::context +//! [surface]: crate::surface +//! [config]: crate::config +//! [EGL]: crate::api::egl + +#![deny(rust_2018_idioms)] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::all)] #![deny(missing_debug_implementations)] -#![allow(clippy::missing_safety_doc, clippy::too_many_arguments)] +#![deny(missing_docs)] #![cfg_attr(feature = "cargo-clippy", deny(warnings))] -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[macro_use] -extern crate objc; +#[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; -mod api; -mod context; -mod platform_impl; -mod windowed; - -pub use crate::context::*; -pub use crate::windowed::*; -pub use winit::*; - -use winit::error::OsError; - -use std::io; - -/// An object that allows you to build [`Context`]s, [`RawContext`]s and -/// [`WindowedContext`]s. -/// -/// One notable limitation of the Wayland backend when it comes to shared -/// [`Context`]s is that both contexts must use the same events loop. -#[derive(Debug, Clone)] -pub struct ContextBuilder<'a, T: ContextCurrentState> { - /// The attributes to use to create the context. - pub gl_attr: GlAttributes<&'a Context>, - /// The pixel format requirements - pub pf_reqs: PixelFormatRequirements, -} - -impl Default for ContextBuilder<'_, NotCurrent> { - fn default() -> Self { - Self { gl_attr: Default::default(), pf_reqs: Default::default() } - } -} - -impl<'a> ContextBuilder<'a, NotCurrent> { - /// Initializes a new `ContextBuilder` with default values. - pub fn new() -> Self { - Default::default() - } -} - -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Sets how the backend should choose the OpenGL API and version. - #[inline] - pub fn with_gl(mut self, request: GlRequest) -> Self { - self.gl_attr.version = request; - self - } - - /// Sets the desired OpenGL [`Context`] profile. - #[inline] - pub fn with_gl_profile(mut self, profile: GlProfile) -> Self { - self.gl_attr.profile = Some(profile); - self - } - - /// Sets the *debug* flag for the OpenGL [`Context`]. - /// - /// The default value for this flag is `cfg!(debug_assertions)`, which means - /// that it's enabled when you run `cargo build` and disabled when you run - /// `cargo build --release`. - #[inline] - pub fn with_gl_debug_flag(mut self, flag: bool) -> Self { - self.gl_attr.debug = flag; - self - } - - /// Sets the robustness of the OpenGL [`Context`]. See the docs of - /// [`Robustness`]. - #[inline] - pub fn with_gl_robustness(mut self, robustness: Robustness) -> Self { - self.gl_attr.robustness = robustness; - self - } - - /// Requests that the window has vsync enabled. - /// - /// By default, vsync is not enabled. - #[inline] - pub fn with_vsync(mut self, vsync: bool) -> Self { - self.gl_attr.vsync = vsync; - self - } +#[cfg(any(egl_backend, glx_backend))] +mod lib_loading; - /// Share the display lists with the given [`Context`]. - #[inline] - pub fn with_shared_lists( - self, - other: &'a Context, - ) -> ContextBuilder<'a, T2> { - ContextBuilder { gl_attr: self.gl_attr.set_sharing(Some(other)), pf_reqs: self.pf_reqs } - } +#[cfg(cgl_backend)] +#[macro_use] +extern crate objc; - /// Sets the multisampling level to request. A value of `0` indicates that - /// multisampling must not be enabled. - /// - /// # Panic - /// - /// Will panic if `samples` is not a power of two. - #[inline] - pub fn with_multisampling(mut self, samples: u16) -> Self { - self.pf_reqs.multisampling = match samples { - 0 => None, - _ => { - assert!(samples.is_power_of_two()); - Some(samples) +pub(crate) mod private { + /// Prevent traits from being implemented downstream, since those are used + /// purely for documentation organization and simplify platform api + /// implementation maintaining. + 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), } }; - self - } - - /// Sets the number of bits in the depth buffer. - #[inline] - pub fn with_depth_buffer(mut self, bits: u8) -> Self { - self.pf_reqs.depth_bits = Some(bits); - self - } - - /// Sets the number of bits in the stencil buffer. - #[inline] - pub fn with_stencil_buffer(mut self, bits: u8) -> Self { - self.pf_reqs.stencil_bits = Some(bits); - self - } - - /// Sets the number of bits in the color buffer. - #[inline] - pub fn with_pixel_format(mut self, color_bits: u8, alpha_bits: u8) -> Self { - self.pf_reqs.color_bits = Some(color_bits); - self.pf_reqs.alpha_bits = Some(alpha_bits); - self - } - - /// Request the backend to be stereoscopic. - #[inline] - pub fn with_stereoscopy(mut self) -> Self { - self.pf_reqs.stereoscopy = true; - self - } - - /// Sets whether sRGB should be enabled on the window. - /// - /// The default value is [`true`]. - #[inline] - pub fn with_srgb(mut self, srgb_enabled: bool) -> Self { - self.pf_reqs.srgb = srgb_enabled; - self - } - - /// Sets whether double buffering should be enabled. - /// - /// The default value is [`None`]. - /// - /// ## Platform-specific - /// - /// This option will be taken into account on the following platforms: - /// - /// * MacOS - /// * Unix operating systems using GLX with X - /// * Windows using WGL - #[inline] - pub fn with_double_buffer(mut self, double_buffer: Option) -> Self { - self.pf_reqs.double_buffer = double_buffer; - self - } - - /// Sets whether hardware acceleration is required. - /// - /// The default value is `Some(true)` - /// - /// ## Platform-specific - /// - /// This option will be taken into account on the following platforms: - /// - /// * MacOS - /// * Unix operating systems using EGL with either X or Wayland - /// * Windows using EGL or WGL - /// * Android using EGL - #[inline] - pub fn with_hardware_acceleration(mut self, acceleration: Option) -> Self { - self.pf_reqs.hardware_accelerated = acceleration; - self - } -} - -/// Error that can happen while creating a window or a headless renderer. -#[derive(Debug)] -pub enum CreationError { - OsError(String), - NotSupported(String), - NoBackendAvailable(Box), - RobustnessNotSupported, - OpenGlVersionNotSupported, - NoAvailablePixelFormat, - PlatformSpecific(String), - Window(OsError), - /// We received multiple errors, instead of one. - CreationErrors(Vec>), -} - -impl CreationError { - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - #[cfg(feature = "x11")] - pub(crate) fn append(self, err: CreationError) -> Self { - match self { - CreationError::CreationErrors(mut errs) => { - errs.push(Box::new(err)); - CreationError::CreationErrors(errs) - } - _ => CreationError::CreationErrors(vec![Box::new(err), Box::new(self)]), - } - } -} - -impl std::fmt::Display for CreationError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - f.write_str(match self { - CreationError::OsError(text) - | CreationError::NotSupported(text) - | CreationError::PlatformSpecific(text) => text, - CreationError::NoBackendAvailable(err) => { - return write!(f, "No backend is available: {}", err); - } - CreationError::RobustnessNotSupported => { - "You requested robustness, but it is not supported." - } - CreationError::OpenGlVersionNotSupported => { - "The requested OpenGL version is not supported." - } - CreationError::NoAvailablePixelFormat => { - "Couldn't find any pixel format that matches the criteria." + ($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, } - CreationError::Window(err) => { - return write!(f, "{}", err); - } - CreationError::CreationErrors(ref es) => { - writeln!(f, "Received multiple errors:")?; - for e in es { - writeln!(f, "\t{}", e)?; - } - return Ok(()); - } - }) - } -} - -impl std::error::Error for CreationError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - CreationError::NoBackendAvailable(err) => Some(&**err), - CreationError::Window(err) => Some(err), - _ => None, - } - } -} - -impl From for CreationError { - fn from(err: OsError) -> Self { - CreationError::Window(err) - } -} - -/// Error that can happen when manipulating an OpenGL [`Context`]. -#[derive(Debug)] -pub enum ContextError { - /// General platform error. - OsError(String), - IoError(io::Error), - ContextLost, - FunctionUnavailable, -} - -impl std::fmt::Display for ContextError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - match self { - ContextError::OsError(string) => write!(formatter, "{}", string), - ContextError::IoError(err) => write!(formatter, "{}", err), - ContextError::ContextLost => write!(formatter, "Context lost"), - ContextError::FunctionUnavailable => write!(formatter, "Function unavailable"), - } - } -} - -impl std::error::Error for ContextError {} - -/// All APIs related to OpenGL that you can possibly get while using glutin. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Api { - /// The classical OpenGL. Available on Windows, Unix operating systems, - /// OS/X. - OpenGl, - /// OpenGL embedded system. Available on Unix operating systems, Android. - OpenGlEs, - /// OpenGL for the web. Very similar to OpenGL ES. - WebGl, -} - -/// Describes the requested OpenGL [`Context`] profiles. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum GlProfile { - /// Include all the immediate more functions and definitions. - Compatibility, - /// Include all the future-compatible functions and definitions. - Core, -} - -/// Describes the OpenGL API and version that are being requested when a context -/// is created. -#[derive(Debug, Copy, Clone)] -pub enum GlRequest { - /// Request the latest version of the "best" API of this platform. - /// - /// On desktop, will try OpenGL. - Latest, - - /// Request a specific version of a specific API. - /// - /// Example: `GlRequest::Specific(Api::OpenGl, (3, 3))`. - Specific(Api, (u8, u8)), - - /// If OpenGL is available, create an OpenGL [`Context`] with the specified - /// `opengl_version`. Else if OpenGL ES or WebGL is available, create a - /// context with the specified `opengles_version`. - GlThenGles { - /// The version to use for OpenGL. - opengl_version: (u8, u8), - /// The version to use for OpenGL ES. - opengles_version: (u8, u8), - }, -} - -impl GlRequest { - /// Extract the desktop GL version, if any. - pub fn to_gl_version(self) -> Option<(u8, u8)> { - match self { - GlRequest::Specific(Api::OpenGl, opengl_version) => Some(opengl_version), - GlRequest::GlThenGles { opengl_version, .. } => Some(opengl_version), - _ => None, - } - } -} - -/// The minimum core profile GL context. Useful for getting the minimum -/// required GL version while still running on OSX, which often forbids -/// the compatibility profile features. -pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2)); - -/// Specifies the tolerance of the OpenGL [`Context`] to faults. If you accept -/// raw OpenGL commands and/or raw shader code from an untrusted source, you -/// should definitely care about this. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Robustness { - /// Not everything is checked. Your application can crash if you do - /// something wrong with your shaders. - NotRobust, - - /// The driver doesn't check anything. This option is very dangerous. - /// Please know what you're doing before using it. See the - /// `GL_KHR_no_error` extension. - /// - /// Since this option is purely an optimization, no error will be returned - /// if the backend doesn't support it. Instead it will automatically - /// fall back to [`NotRobust`][Self::NotRobust]. - NoError, - - /// Everything is checked to avoid any crash. The driver will attempt to - /// avoid any problem, but if a problem occurs the behavior is - /// implementation-defined. You are just guaranteed not to get a crash. - RobustNoResetNotification, - - /// Same as [`RobustNoResetNotification`][Self::RobustNoResetNotification] - /// but the context creation doesn't fail if it's not supported. - TryRobustNoResetNotification, - - /// Everything is checked to avoid any crash. If a problem occurs, the - /// context will enter a "context lost" state. It must then be - /// recreated. For the moment, glutin doesn't provide a way to recreate - /// a context with the same window :-/ - RobustLoseContextOnReset, - - /// Same as [`RobustLoseContextOnReset`][Self::RobustLoseContextOnReset] - /// but the context creation doesn't fail if it's not supported. - TryRobustLoseContextOnReset, -} - -/// The behavior of the driver when you change the current context. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ReleaseBehavior { - /// Doesn't do anything. Most notably doesn't flush. - None, - - /// Flushes the context that was previously current as if `glFlush` was - /// called. - Flush, -} - -/// Describes a possible format. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct PixelFormat { - pub hardware_accelerated: bool, - /// The number of color bits. Does not include alpha bits. - pub color_bits: u8, - pub alpha_bits: u8, - pub depth_bits: u8, - pub stencil_bits: u8, - pub stereoscopy: bool, - pub double_buffer: bool, - /// [`None`] if multisampling is disabled, otherwise `Some(N)` where `N` is - /// the multisampling level. - pub multisampling: Option, - pub srgb: bool, -} - -/// Describes how the backend should choose a pixel format. -// TODO: swap method? (swap, copy) -#[derive(Clone, Debug)] -pub struct PixelFormatRequirements { - /// If true, only hardware-accelerated formats will be considered. If - /// false, only software renderers. [`None`] means "don't care". Default - /// is `Some(true)`. - pub hardware_accelerated: Option, - - /// Minimum number of bits for the color buffer, excluding alpha. [`None`] - /// means "don't care". The default is `Some(24)`. - pub color_bits: Option, - - /// If true, the color buffer must be in a floating point format. Default - /// is [`false`]. - /// - /// Using floating points allows you to write values outside of the `[0.0, - /// 1.0]` range. - pub float_color_buffer: bool, - - /// Minimum number of bits for the alpha in the color buffer. [`None`] means - /// "don't care". The default is `Some(8)`. - pub alpha_bits: Option, - - /// Minimum number of bits for the depth buffer. [`None`] means "don't care". - /// The default value is `Some(24)`. - pub depth_bits: Option, - - /// Minimum number of stencil bits. [`None`] means "don't care". - /// The default value is `Some(8)`. - pub stencil_bits: Option, - - /// If true, only double-buffered formats will be considered. If false, - /// only single-buffer formats. [`None`] means "don't care". The default - /// is `Some(true)`. - pub double_buffer: Option, - - /// Contains the minimum number of samples per pixel in the color, depth - /// and stencil buffers. [`None`] means "don't care". Default is [`None`]. - /// A value of `Some(0)` indicates that multisampling must not be enabled. - pub multisampling: Option, - - /// If true, only stereoscopic formats will be considered. If false, only - /// non-stereoscopic formats. The default is [`false`]. - pub stereoscopy: bool, - - /// If true, only sRGB-capable formats will be considered. If false, don't - /// care. The default is [`true`]. - pub srgb: bool, - - /// The behavior when changing the current context. Default is `Flush`. - pub release_behavior: ReleaseBehavior, - - /// X11 only: set internally to ensure a certain visual xid is used when - /// choosing the fbconfig. - #[allow(dead_code)] - pub(crate) x11_visual_xid: Option, -} - -impl Default for PixelFormatRequirements { - #[inline] - fn default() -> PixelFormatRequirements { - PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - float_color_buffer: false, - alpha_bits: Some(8), - depth_bits: Some(24), - stencil_bits: Some(8), - double_buffer: None, - multisampling: None, - stereoscopy: false, - srgb: true, - release_behavior: ReleaseBehavior::Flush, - x11_visual_xid: None, - } - } -} - -/// Attributes to use when creating an OpenGL [`Context`]. -#[derive(Clone, Debug)] -pub struct GlAttributes { - /// An existing context with which some OpenGL objects get shared. - /// - /// The default is [`None`]. - pub sharing: Option, - - /// Version to try create. See [`GlRequest`] for more infos. - /// - /// The default is [`GlRequest::Latest`]. - pub version: GlRequest, - - /// OpenGL profile to use. - /// - /// The default is [`None`]. - pub profile: Option, - - /// Whether to enable the `debug` flag of the context. - /// - /// Debug contexts are usually slower but give better error reporting. - /// - /// The default is [`true`] in debug mode and [`false`] in release mode. - pub debug: bool, - - /// How the OpenGL [`Context`] should detect errors. - /// - /// The default is `NotRobust` because this is what is typically expected - /// when you create an OpenGL [`Context`]. However for safety you should - /// consider [`Robustness::TryRobustLoseContextOnReset`]. - pub robustness: Robustness, - - /// Whether to use vsync. If vsync is enabled, calling - /// [`ContextWrapper::swap_buffers()`] will block until the screen refreshes. - /// This is typically used to prevent screen tearing. - /// - /// The default is [`false`]. - pub vsync: bool, -} - -impl GlAttributes { - /// Turns the `sharing` parameter into another type by calling a closure. - #[inline] - pub fn map_sharing(self, f: F) -> GlAttributes - where - F: FnOnce(S) -> T, - { - GlAttributes { - sharing: self.sharing.map(f), - version: self.version, - profile: self.profile, - debug: self.debug, - robustness: self.robustness, - vsync: self.vsync, - } - } - - /// Turns the `sharing` parameter into another type. - #[inline] - fn set_sharing(self, sharing: Option) -> GlAttributes { - GlAttributes { - sharing, - version: self.version, - profile: self.profile, - debug: self.debug, - robustness: self.robustness, - vsync: self.vsync, - } - } -} - -impl Default for GlAttributes { - #[inline] - fn default() -> GlAttributes { - GlAttributes { - sharing: None, - version: GlRequest::Latest, - profile: None, - debug: cfg!(debug_assertions), - robustness: Robustness::NotRobust, - vsync: false, - } + }; } -} -// Rectangles to submit as buffer damage. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Rect { - pub x: u32, - pub y: u32, - pub width: u32, - pub height: u32, + pub(crate) use gl_api_dispatch; } diff --git a/glutin/src/lib_loading.rs b/glutin/src/lib_loading.rs new file mode 100644 index 0000000000..afb84b457a --- /dev/null +++ b/glutin/src/lib_loading.rs @@ -0,0 +1,55 @@ +//! 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 { + /// The library must be unsured to live long enough. + unsafe fn load_with(lib: &Library) -> Self; +} + +#[derive(Clone)] +pub struct SymWrapper { + sym: T, + _lib: Arc, +} + +impl SymWrapper { + pub 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 + } +} diff --git a/glutin/src/platform/android.rs b/glutin/src/platform/android.rs deleted file mode 100644 index 9da067af39..0000000000 --- a/glutin/src/platform/android.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![cfg(target_os = "android")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; - -pub use winit::platform::android::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = EGLContext; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - Some(self.context.get_egl_display()) - } -} diff --git a/glutin/src/platform/ios.rs b/glutin/src/platform/ios.rs deleted file mode 100644 index d9073015b8..0000000000 --- a/glutin/src/platform/ios.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![cfg(target_os = "ios")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; - -pub use winit::platform::ios::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = *mut raw::c_void; - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - None - } -} diff --git a/glutin/src/platform/macos.rs b/glutin/src/platform/macos.rs deleted file mode 100644 index 9e21e9211e..0000000000 --- a/glutin/src/platform/macos.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![cfg(target_os = "macos")] - -use crate::platform::ContextTraitExt; -use crate::{Context, ContextCurrentState}; - -pub use winit::platform::macos::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = *mut raw::c_void; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - None - } -} diff --git a/glutin/src/platform/mod.rs b/glutin/src/platform/mod.rs index 4ceb89a671..47cc0d7292 100644 --- a/glutin/src/platform/mod.rs +++ b/glutin/src/platform/mod.rs @@ -1,53 +1,4 @@ -//! Contains traits with platform-specific methods in them. -//! -//! Contains the following modules: -//! -//! - `android` -//! - `ios` -//! - `macos` -//! - `unix` -//! - `windows` +//! Platform specific utils to simplify interactions with the Api. -/// Platform-specific methods for android. -pub mod android; -/// Platform-specific methods for iOS. -pub mod ios; -/// Platform-specific methods for macOS. -pub mod macos; -/// Platform-specific methods for unix operating systems. -pub mod unix; -/// Platform-specific methods for Windows. -pub mod windows; -/// Platform-specific methods for event loops independent from the application -/// lifetime. -pub mod run_return { - #![cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", - ))] - pub use winit::platform::run_return::*; -} - -use std::os::raw; - -/// Platform-specific extensions for OpenGL [`Context`][crate::Context]s. -pub trait ContextTraitExt { - /// Raw context handle. - type Handle; - - /// Returns the raw context handle. - unsafe fn raw_handle(&self) -> Self::Handle; - - /// Returns a pointer to the `EGLDisplay` object of EGL that is used by this - /// context. - /// - /// Return [`None`] if the context doesn't use EGL. - // The pointer will become invalid when the context is destroyed. - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void>; -} +#[cfg(x11_platform)] +pub mod x11; diff --git a/glutin/src/platform/unix.rs b/glutin/src/platform/unix.rs deleted file mode 100644 index 66aed2c4ad..0000000000 --- a/glutin/src/platform/unix.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -use crate::platform::ContextTraitExt; -pub use crate::platform_impl::{HeadlessContextExt, RawContextExt, RawHandle}; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; -#[cfg(feature = "x11")] -pub use glutin_glx_sys::GLXContext; - -pub use winit::platform::unix::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = RawHandle; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - self.context.get_egl_display() - } -} diff --git a/glutin/src/platform/windows.rs b/glutin/src/platform/windows.rs deleted file mode 100644 index 583ef0d632..0000000000 --- a/glutin/src/platform/windows.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![cfg(target_os = "windows")] - -use crate::platform::ContextTraitExt; -pub use crate::platform_impl::{RawContextExt, RawHandle}; -use crate::{Context, ContextCurrentState}; -pub use glutin_egl_sys::EGLContext; - -pub use winapi::shared::windef::HGLRC; -pub use winit::platform::windows::*; - -use std::os::raw; - -impl ContextTraitExt for Context { - type Handle = RawHandle; - - #[inline] - unsafe fn raw_handle(&self) -> Self::Handle { - self.context.raw_handle() - } - - #[inline] - unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - self.context.get_egl_display() - } -} diff --git a/glutin/src/platform/x11.rs b/glutin/src/platform/x11.rs new file mode 100644 index 0000000000..a34654d74a --- /dev/null +++ b/glutin/src/platform/x11.rs @@ -0,0 +1,95 @@ +//! Dedicated utils to work with X11 shenanigans. + +use std::mem; + +use once_cell::sync::Lazy; +use x11_dl::xlib::{Display, XVisualInfo, Xlib}; +#[cfg(egl_backend)] +use x11_dl::xlib::{VisualIDMask, XID}; +use x11_dl::xrender::Xrender; + +/// 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 options out of the config. +pub trait X11GlConfigExt { + /// The `X11VisualInfo` that must be used to inititalize the Xlib window. + fn x11_visual(&self) -> Option; +} + +/// The X11 visual info. +/// +/// This must be used when building X11 window, so it'll be compatible with the +/// underlying Api. +#[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 mut raw: XVisualInfo = std::mem::zeroed(); + raw.visualid = xid; + + let mut num_visuals = 0; + let raw = (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals); + + if raw.is_null() { + return None; + } + + 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 + } + + /// 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 _); + } + } +} diff --git a/glutin/src/platform_impl/android/mod.rs b/glutin/src/platform_impl/android/mod.rs deleted file mode 100644 index 5c6fb0a92c..0000000000 --- a/glutin/src/platform_impl/android/mod.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![cfg(target_os = "android")] - -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType}; -use crate::CreationError::{self, OsError}; -use crate::{Api, ContextError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect}; - -use glutin_egl_sys as ffi; -use parking_lot::Mutex; -use raw_window_handle::{AndroidNdkWindowHandle, HasRawWindowHandle, RawWindowHandle}; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::WindowBuilder; - -use std::sync::Arc; - -#[derive(Debug)] -struct AndroidContext { - egl_context: EglContext, - stopped: Option>, -} - -#[derive(Debug)] -pub struct Context(Arc); - -impl Context { - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result<(winit::window::Window, Self), CreationError> { - let win = wb.build(el)?; - let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); - let nwin = - if let RawWindowHandle::AndroidNdk(AndroidNdkWindowHandle { a_native_window, .. }) = - win.raw_window_handle() - { - a_native_window - } else { - return Err(OsError("raw_window_handle() is not for Android".to_string())); - }; - let native_display = NativeDisplay::Android; - let egl_context = - EglContext::new(pf_reqs, &gl_attr, native_display, EglSurfaceType::Window, |c, _| { - Ok(c[0]) - }) - .and_then(|p| p.finish(nwin))?; - let ctx = Arc::new(AndroidContext { egl_context, stopped: Some(Mutex::new(false)) }); - - let context = Context(ctx); - - Ok((win, context)) - } - - #[inline] - pub fn new_headless( - _el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); - let context = EglContext::new( - pf_reqs, - &gl_attr, - NativeDisplay::Android, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - )?; - let egl_context = context.finish_pbuffer(size)?; - let ctx = Arc::new(AndroidContext { egl_context, stopped: None }); - Ok(Context(ctx)) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - - self.0.egl_context.make_current() - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - - self.0.egl_context.make_not_current() - } - - #[inline] - pub fn resize(&self, _: u32, _: u32) {} - - #[inline] - pub fn is_current(&self) -> bool { - self.0.egl_context.is_current() - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.0.egl_context.get_proc_address(addr) - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - self.0.egl_context.buffer_age() - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - self.0.egl_context.swap_buffers() - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - if let Some(ref stopped) = self.0.stopped { - let stopped = stopped.lock(); - if *stopped { - return Err(ContextError::ContextLost); - } - } - self.0.egl_context.swap_buffers_with_damage(rects) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - self.0.egl_context.swap_buffers_with_damage_supported() - } - - #[inline] - pub fn get_api(&self) -> Api { - self.0.egl_context.get_api() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - self.0.egl_context.get_pixel_format() - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::EGLContext { - self.0.egl_context.raw_handle() - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> ffi::EGLDisplay { - self.0.egl_context.get_egl_display() - } -} diff --git a/glutin/src/platform_impl/ios/mod.rs b/glutin/src/platform_impl/ios/mod.rs deleted file mode 100644 index fddec561b3..0000000000 --- a/glutin/src/platform_impl/ios/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![cfg(target_os = "ios")] - -pub use crate::api::ios::*; -pub use glutin_gles2_sys::id; diff --git a/glutin/src/platform_impl/macos/helpers.rs b/glutin/src/platform_impl/macos/helpers.rs deleted file mode 100644 index 26c01c48a6..0000000000 --- a/glutin/src/platform_impl/macos/helpers.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::cmp::Ordering; - -use crate::{ - CreationError, GlAttributes, GlProfile, GlRequest, PixelFormatRequirements, ReleaseBehavior, -}; - -use cocoa::appkit::*; -use cocoa::base::nil; - -pub fn get_gl_profile( - opengl: &GlAttributes<&T>, - pf_reqs: &PixelFormatRequirements, -) -> Result { - let version = opengl.version.to_gl_version(); - // first, compatibility profile support is strict - if opengl.profile == Some(GlProfile::Compatibility) { - // Note: we are not using ranges because of a rust bug that should be - // fixed here: https://github.com/rust-lang/rust/pull/27050 - if version.unwrap_or((2, 1)) < (3, 2) { - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } else if let Some(v) = version { - // second, process exact requested version, if any - match v.cmp(&(3, 2)) { - Ordering::Less => { - if opengl.profile.is_none() && v <= (2, 1) { - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } - } - Ordering::Equal => Ok(NSOpenGLProfileVersion3_2Core), - Ordering::Greater => Ok(NSOpenGLProfileVersion4_1Core), - } - } else if let GlRequest::Latest = opengl.version { - // now, find the latest supported version automatically; - let mut attributes: [u32; 6] = [0; 6]; - let mut current_idx = 0; - attributes[current_idx] = NSOpenGLPFAAllowOfflineRenderers as u32; - current_idx += 1; - - if let Some(true) = pf_reqs.hardware_accelerated { - attributes[current_idx] = NSOpenGLPFAAccelerated as u32; - current_idx += 1; - } - - if pf_reqs.double_buffer != Some(false) { - attributes[current_idx] = NSOpenGLPFADoubleBuffer as u32; - current_idx += 1 - } - - attributes[current_idx] = NSOpenGLPFAOpenGLProfile as u32; - current_idx += 1; - - for &profile in &[NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersion3_2Core] { - attributes[current_idx] = profile as u32; - let id = unsafe { NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes) }; - if id != nil { - unsafe { msg_send![id, release] } - return Ok(profile); - } - } - // nothing else to do - Ok(NSOpenGLProfileVersionLegacy) - } else { - Err(CreationError::OpenGlVersionNotSupported) - } -} - -pub fn build_nsattributes( - pf_reqs: &PixelFormatRequirements, - profile: NSOpenGLPFAOpenGLProfiles, -) -> Result, CreationError> { - // NOTE: OS X no longer has the concept of setting individual - // color component's bit size. Instead we can only specify the - // full color size and hope for the best. Another hiccup is that - // `NSOpenGLPFAColorSize` also includes `NSOpenGLPFAAlphaSize`, - // so we have to account for that as well. - let alpha_depth = pf_reqs.alpha_bits.unwrap_or(8); - let color_depth = pf_reqs.color_bits.unwrap_or(24) + alpha_depth; - - let mut attributes = vec![ - NSOpenGLPFAOpenGLProfile as u32, - profile as u32, - NSOpenGLPFAClosestPolicy as u32, - NSOpenGLPFAColorSize as u32, - color_depth as u32, - NSOpenGLPFAAlphaSize as u32, - alpha_depth as u32, - NSOpenGLPFADepthSize as u32, - pf_reqs.depth_bits.unwrap_or(24) as u32, - NSOpenGLPFAStencilSize as u32, - pf_reqs.stencil_bits.unwrap_or(8) as u32, - NSOpenGLPFAAllowOfflineRenderers as u32, - ]; - - if let Some(true) = pf_reqs.hardware_accelerated { - attributes.push(NSOpenGLPFAAccelerated as u32); - } - - // Note: according to Apple docs, not specifying `NSOpenGLPFADoubleBuffer` - // equals to requesting a single front buffer, in which case most of the GL - // renderers will show nothing, since they draw to GL_BACK. - if pf_reqs.double_buffer != Some(false) { - attributes.push(NSOpenGLPFADoubleBuffer as u32); - } - - if pf_reqs.release_behavior != ReleaseBehavior::Flush { - return Err(CreationError::NoAvailablePixelFormat); - } - - if pf_reqs.stereoscopy { - unimplemented!(); // TODO: - } - - if pf_reqs.float_color_buffer { - attributes.push(NSOpenGLPFAColorFloat as u32); - } - - if let Some(samples) = pf_reqs.multisampling { - attributes.push(NSOpenGLPFAMultisample as u32); - attributes.push(NSOpenGLPFASampleBuffers as u32); - attributes.push(1); - attributes.push(NSOpenGLPFASamples as u32); - attributes.push(samples as u32); - } - - // attribute list must be null terminated. - attributes.push(0); - - Ok(attributes) -} diff --git a/glutin/src/platform_impl/macos/mod.rs b/glutin/src/platform_impl/macos/mod.rs deleted file mode 100644 index 6b67085c64..0000000000 --- a/glutin/src/platform_impl/macos/mod.rs +++ /dev/null @@ -1,358 +0,0 @@ -#![cfg(target_os = "macos")] -#![allow(clippy::let_unit_value)] -use crate::{ - ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect, - Robustness, -}; - -use cgl::{kCGLCECrashOnRemovedFunctions, kCGLCPSurfaceOpacity, CGLEnable, CGLSetParameter}; -use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat}; -use cocoa::base::{id, nil}; -use cocoa::foundation::NSAutoreleasePool; -use core_foundation::base::TCFType; -use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; -use core_foundation::string::CFString; -use objc::runtime::{BOOL, NO}; - -use crate::platform::macos::WindowExtMacOS; - -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::Deref; -use std::os::raw; -use std::str::FromStr; - -mod helpers; - -#[derive(Debug)] -pub enum Context { - WindowedContext(WindowedContext), - HeadlessContext(HeadlessContext), -} - -#[derive(Debug)] -pub struct WindowedContext { - // NSOpenGLContext - context: IdRef, - pixel_format: PixelFormat, -} - -#[derive(Debug)] -pub struct HeadlessContext { - context: IdRef, -} - -impl Context { - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - let transparent = wb.transparent(); - let win = wb.build(el)?; - - let share_ctx = gl_attr.sharing.map_or(nil, |c| *c.get_id()); - - match gl_attr.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - - let view = win.ns_view() as id; - - let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; - let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; - unsafe { - let pixel_format = - IdRef::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes)); - let pixel_format = match pixel_format.non_nil() { - None => return Err(CreationError::NoAvailablePixelFormat), - Some(pf) => pf, - }; - - let gl_context = IdRef::new( - NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixel_format, share_ctx), - ); - let gl_context = match gl_context.non_nil() { - Some(gl_context) => gl_context, - None => { - return Err(CreationError::NotSupported( - "could not open gl context".to_string(), - )); - } - }; - - let pixel_format = { - let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 { - let mut value = 0; - NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( - *pixel_format, - &mut value, - attrib, - NSOpenGLContext::currentVirtualScreen(*gl_context), - ); - value - }; - - PixelFormat { - hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0, - color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - - get_attr(appkit::NSOpenGLPFAAlphaSize)) - as u8, - alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8, - depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8, - stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8, - stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0, - double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0, - multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 { - Some(get_attr(appkit::NSOpenGLPFASamples) as u16) - } else { - None - }, - srgb: true, - } - }; - - gl_context.setView_(view); - let value = if gl_attr.vsync { 1 } else { 0 }; - gl_context.setValues_forParameter_( - &value, - appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval, - ); - - if transparent { - let opacity = 0; - CGLSetParameter( - gl_context.CGLContextObj() as *mut _, - kCGLCPSurfaceOpacity, - &opacity, - ); - } - - CGLEnable(gl_context.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions); - - let context = WindowedContext { context: gl_context, pixel_format }; - Ok((win, Context::WindowedContext(context))) - } - } - - #[inline] - pub fn new_headless( - _el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - _size: dpi::PhysicalSize, - ) -> Result { - let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; - let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; - let context = unsafe { - let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes); - if pixelformat == nil { - return Err(CreationError::OsError( - "Could not create the pixel format".to_string(), - )); - } - let context = - NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil); - if context == nil { - return Err(CreationError::OsError( - "Could not create the rendering context".to_string(), - )); - } - - IdRef::new(context) - }; - - let headless = HeadlessContext { context }; - - Ok(Context::HeadlessContext(headless)) - } - - pub fn resize(&self, _width: u32, _height: u32) { - match *self { - Context::WindowedContext(ref c) => unsafe { c.context.update() }, - _ => unreachable!(), - } - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - Context::WindowedContext(ref c) => { - let _: () = msg_send![*c.context, update]; - c.context.makeCurrentContext(); - } - Context::HeadlessContext(ref c) => { - let _: () = msg_send![*c.context, update]; - c.context.makeCurrentContext(); - } - } - Ok(()) - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - if self.is_current() { - match *self { - Context::WindowedContext(ref c) => { - let _: () = msg_send![*c.context, update]; - NSOpenGLContext::clearCurrentContext(nil); - } - Context::HeadlessContext(ref c) => { - let _: () = msg_send![*c.context, update]; - NSOpenGLContext::clearCurrentContext(nil); - } - } - } - Ok(()) - } - - #[inline] - pub fn is_current(&self) -> bool { - unsafe { - let context = match *self { - Context::WindowedContext(ref c) => *c.context, - Context::HeadlessContext(ref c) => *c.context, - }; - - let pool = NSAutoreleasePool::new(nil); - let current = NSOpenGLContext::currentContext(nil); - let res = if current != nil { - let is_equal: BOOL = msg_send![current, isEqual: context]; - is_equal != NO - } else { - false - }; - let _: () = msg_send![pool, release]; - res - } - } - - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - let symbol_name: CFString = FromStr::from_str(addr).unwrap(); - let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); - let framework = - unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) }; - let symbol = unsafe { - CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) - }; - symbol as *const _ - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - unsafe { - match *self { - Context::WindowedContext(ref c) => { - let pool = NSAutoreleasePool::new(nil); - c.context.flushBuffer(); - let _: () = msg_send![pool, release]; - } - Context::HeadlessContext(_) => unreachable!(), - } - } - Ok(()) - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - 0 - } - - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false - } - - #[inline] - pub fn get_api(&self) -> crate::Api { - crate::Api::OpenGl - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - Context::WindowedContext(ref c) => c.pixel_format.clone(), - Context::HeadlessContext(_) => unreachable!(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> *mut raw::c_void { - match self { - Context::WindowedContext(c) => c.context.deref().CGLContextObj() as *mut _, - Context::HeadlessContext(c) => c.context.deref().CGLContextObj() as *mut _, - } - } - - #[inline] - fn get_id(&self) -> IdRef { - match self { - Context::WindowedContext(w) => w.context.clone(), - Context::HeadlessContext(h) => h.context.clone(), - } - } -} - -#[derive(Debug)] -struct IdRef(id); - -impl IdRef { - fn new(i: id) -> IdRef { - IdRef(i) - } - - #[allow(dead_code)] - fn retain(i: id) -> IdRef { - if i != nil { - let _: id = unsafe { msg_send![i, retain] }; - } - IdRef(i) - } - - fn non_nil(self) -> Option { - if self.0 == nil { - None - } else { - Some(self) - } - } -} - -impl Drop for IdRef { - fn drop(&mut self) { - if self.0 != nil { - let _: () = unsafe { msg_send![self.0, release] }; - } - } -} - -impl Deref for IdRef { - type Target = id; - fn deref(&self) -> &id { - &self.0 - } -} - -impl Clone for IdRef { - fn clone(&self) -> IdRef { - if self.0 != nil { - let _: id = unsafe { msg_send![self.0, retain] }; - } - IdRef(self.0) - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} diff --git a/glutin/src/platform_impl/mod.rs b/glutin/src/platform_impl/mod.rs deleted file mode 100644 index 7d6c795393..0000000000 --- a/glutin/src/platform_impl/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub use self::platform::*; - -#[cfg(target_os = "windows")] -#[path = "windows/mod.rs"] -mod platform; -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] -#[path = "unix/mod.rs"] -mod platform; -#[cfg(target_os = "macos")] -#[path = "macos/mod.rs"] -mod platform; -#[cfg(target_os = "android")] -#[path = "android/mod.rs"] -mod platform; -#[cfg(target_os = "ios")] -#[path = "ios/mod.rs"] -mod platform; diff --git a/glutin/src/platform_impl/unix/mod.rs b/glutin/src/platform_impl/unix/mod.rs deleted file mode 100644 index ef72e8a68b..0000000000 --- a/glutin/src/platform_impl/unix/mod.rs +++ /dev/null @@ -1,461 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", -))] - -#[cfg(not(any(feature = "x11", feature = "wayland")))] -compile_error!("at least one of the 'x11' or 'wayland' features must be enabled"); - -mod wayland; -mod x11; - -#[cfg(feature = "x11")] -use self::x11::X11Context; -use crate::api::osmesa; -use crate::{ - Api, ContextCurrentState, ContextError, CreationError, GlAttributes, NotCurrent, PixelFormat, - PixelFormatRequirements, Rect, -}; -#[cfg(feature = "x11")] -pub use x11::utils as x11_utils; - -#[cfg(feature = "x11")] -use crate::platform::unix::x11::XConnection; -use crate::platform::unix::EventLoopWindowTargetExtUnix; -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::marker::PhantomData; -use std::os::raw; -#[cfg(feature = "x11")] -use std::sync::Arc; - -/// Context handles available on Unix-like platforms. -#[derive(Clone, Debug)] -pub enum RawHandle { - /// Context handle for a glx context. - #[cfg(feature = "x11")] - Glx(glutin_glx_sys::GLXContext), - /// Context handle for a egl context. - Egl(glutin_egl_sys::EGLContext), -} - -#[derive(Debug)] -pub enum ContextType { - #[cfg(feature = "x11")] - X11, - #[cfg(feature = "wayland")] - Wayland, - OsMesa, -} - -#[derive(Debug)] -pub enum Context { - #[cfg(feature = "x11")] - X11(x11::Context), - #[cfg(feature = "wayland")] - Wayland(wayland::Context), - OsMesa(osmesa::OsMesaContext), -} - -impl Context { - fn is_compatible(c: &Option<&Context>, ct: ContextType) -> Result<(), CreationError> { - if let Some(c) = *c { - match ct { - ContextType::OsMesa => match *c { - Context::OsMesa(_) => Ok(()), - _ => { - let msg = "Cannot share an OSMesa context with a non-OSMesa context"; - Err(CreationError::PlatformSpecific(msg.into())) - } - }, - #[cfg(feature = "x11")] - ContextType::X11 => match *c { - Context::X11(_) => Ok(()), - _ => { - let msg = "Cannot share an X11 context with a non-X11 context"; - Err(CreationError::PlatformSpecific(msg.into())) - } - }, - #[cfg(feature = "wayland")] - ContextType::Wayland => match *c { - Context::Wayland(_) => Ok(()), - _ => { - let msg = "Cannot share a Wayland context with a non-Wayland context"; - Err(CreationError::PlatformSpecific(msg.into())) - } - }, - } - } else { - Ok(()) - } - } - - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - #[cfg(feature = "wayland")] - if el.is_wayland() { - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - return wayland::Context::new(wb, el, pf_reqs, &gl_attr) - .map(|(win, context)| (win, Context::Wayland(context))); - } - #[cfg(feature = "x11")] - if el.is_x11() { - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - return x11::Context::new(wb, el, pf_reqs, &gl_attr) - .map(|(win, context)| (win, Context::X11(context))); - } - panic!("glutin was not compiled with support for this display server") - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - Self::new_headless_impl(el, pf_reqs, gl_attr, Some(size)) - } - - pub fn new_headless_impl( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - #[cfg(feature = "wayland")] - if el.is_wayland() { - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - return wayland::Context::new_headless(el, pf_reqs, &gl_attr, size) - .map(Context::Wayland); - } - #[cfg(feature = "x11")] - if el.is_x11() { - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - return x11::Context::new_headless(el, pf_reqs, &gl_attr, size).map(Context::X11); - } - panic!("glutin was not compiled with support for this display server") - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.make_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.make_current(), - Context::OsMesa(ref ctx) => ctx.make_current(), - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.make_not_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.make_not_current(), - Context::OsMesa(ref ctx) => ctx.make_not_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.is_current(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.is_current(), - Context::OsMesa(ref ctx) => ctx.is_current(), - } - } - - #[inline] - pub fn get_api(&self) -> Api { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_api(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_api(), - Context::OsMesa(ref ctx) => ctx.get_api(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => match *ctx.raw_handle() { - X11Context::Glx(ref ctx) => RawHandle::Glx(ctx.raw_handle()), - X11Context::Egl(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - }, - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - Context::OsMesa(ref ctx) => RawHandle::Egl(ctx.raw_handle()), - } - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_egl_display(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_egl_display(), - _ => None, - } - } - - #[inline] - pub fn resize(&self, width: u32, height: u32) { - #![allow(unused)] - match *self { - #[cfg(feature = "x11")] - Context::X11(_) => (), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.resize(width, height), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_proc_address(addr), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_proc_address(addr), - Context::OsMesa(ref ctx) => ctx.get_proc_address(addr), - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers(), - _ => unreachable!(), - } - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers_with_damage(rects), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers_with_damage(rects), - _ => unreachable!(), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.buffer_age(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.buffer_age(), - _ => unreachable!(), - } - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.swap_buffers_with_damage_supported(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.swap_buffers_with_damage_supported(), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - #[cfg(feature = "x11")] - Context::X11(ref ctx) => ctx.get_pixel_format(), - #[cfg(feature = "wayland")] - Context::Wayland(ref ctx) => ctx.get_pixel_format(), - _ => unreachable!(), - } - } -} - -/// A unix-specific extension to the [`ContextBuilder`][crate::ContextBuilder] -/// which allows building unix-specific headless contexts. -pub trait HeadlessContextExt { - /// Builds an OsMesa context. - /// - /// Errors can occur if the OpenGL [`Context`][crate::Context] could not be created. - /// This generally happens because the underlying platform doesn't support a - /// requested feature. - fn build_osmesa( - self, - size: dpi::PhysicalSize, - ) -> Result, CreationError> - where - Self: Sized; - - /// Builds an EGL-surfaceless context. - /// - /// Errors can occur if the OpenGL [`Context`][crate::Context] could not be created. - /// This generally happens because the underlying platform doesn't support a - /// requested feature. - fn build_surfaceless( - self, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> HeadlessContextExt for crate::ContextBuilder<'a, T> { - #[inline] - fn build_osmesa( - self, - size: dpi::PhysicalSize, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::OsMesa)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::OsMesa(ref ctx) => ctx, - _ => unreachable!(), - }); - osmesa::OsMesaContext::new(&pf_reqs, &gl_attr, size) - .map(Context::OsMesa) - .map(|context| crate::Context { context, phantom: PhantomData }) - } - - #[inline] - fn build_surfaceless( - self, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::new_headless_impl(el, &pf_reqs, &gl_attr, None) - .map(|context| crate::Context { context, phantom: PhantomData }) - } -} - -/// A unix-specific extension for the [`ContextBuilder`][crate::ContextBuilder] -/// which allows assembling [`RawContext`][crate::RawContext]s. -pub trait RawContextExt { - /// Creates a raw context on the provided surface. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The surface/display_ptr is destroyed before the context - #[cfg(feature = "wayland")] - unsafe fn build_raw_wayland_context( - self, - display_ptr: *const wayland::wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - ) -> Result, CreationError> - where - Self: Sized; - - /// Creates a raw context on the provided window. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The xwin is destroyed before the context - #[cfg(feature = "x11")] - unsafe fn build_raw_x11_context( - self, - xconn: Arc, - xwin: raw::c_ulong, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> RawContextExt for crate::ContextBuilder<'a, T> { - #[inline] - #[cfg(feature = "wayland")] - unsafe fn build_raw_wayland_context( - self, - display_ptr: *const wayland::wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Wayland(ref ctx) => ctx, - _ => unreachable!(), - }); - wayland::Context::new_raw_context(display_ptr, surface, width, height, &pf_reqs, &gl_attr) - .map(Context::Wayland) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) - } - - #[inline] - #[cfg(feature = "x11")] - unsafe fn build_raw_x11_context( - self, - xconn: Arc, - xwin: raw::c_ulong, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::X11(ref ctx) => ctx, - _ => unreachable!(), - }); - x11::Context::new_raw_context(xconn, xwin, &pf_reqs, &gl_attr) - .map(Context::X11) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) - } -} diff --git a/glutin/src/platform_impl/unix/wayland.rs b/glutin/src/platform_impl/unix/wayland.rs deleted file mode 100644 index bfebe469c6..0000000000 --- a/glutin/src/platform_impl/unix/wayland.rs +++ /dev/null @@ -1,195 +0,0 @@ -#![cfg(feature = "wayland")] - -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType}; -use crate::{ - ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect, -}; - -use crate::platform::unix::{EventLoopWindowTargetExtUnix, WindowExtUnix}; -use glutin_egl_sys as ffi; -pub use wayland_client::sys::client::wl_display; - -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::Deref; -use std::os::raw; -use std::sync::Arc; - -pub struct EglSurface(Arc); - -impl std::fmt::Debug for EglSurface { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "EglSurface(...)") - } -} - -#[derive(Debug)] -pub enum Context { - Windowed(EglContext, EglSurface), - PBuffer(EglContext), - Surfaceless(EglContext), -} - -impl Deref for Context { - type Target = EglContext; - - fn deref(&self) -> &Self::Target { - match self { - Context::Windowed(ctx, _) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Surfaceless(ctx) => ctx, - } - } -} - -impl Context { - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - let gl_attr = gl_attr.clone().map_sharing(|c| &**c); - let display_ptr = el.wayland_display().unwrap() as *const _; - let native_display = NativeDisplay::Wayland(Some(display_ptr as *const _)); - if let Some(size) = size { - let context = EglContext::new( - pf_reqs, - &gl_attr, - native_display, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish_pbuffer(size))?; - let context = Context::PBuffer(context); - Ok(context) - } else { - // Surfaceless - let context = EglContext::new( - pf_reqs, - &gl_attr, - native_display, - EglSurfaceType::Surfaceless, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish_surfaceless())?; - let context = Context::Surfaceless(context); - Ok(context) - } - } - - #[inline] - pub fn new( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - let win = wb.build(el)?; - - let size = win.inner_size(); - let (width, height): (u32, u32) = size.into(); - - let display_ptr = win.wayland_display().unwrap() as *const _; - let surface = win.wayland_surface(); - let surface = match surface { - Some(s) => s, - None => { - return Err(CreationError::NotSupported("Wayland not found".to_string())); - } - }; - - let context = Self::new_raw_context(display_ptr, surface, width, height, pf_reqs, gl_attr)?; - Ok((win, context)) - } - - #[inline] - pub fn new_raw_context( - display_ptr: *const wl_display, - surface: *mut raw::c_void, - width: u32, - height: u32, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result { - let egl_surface = unsafe { - wayland_egl::WlEglSurface::new_from_raw(surface as *mut _, width as i32, height as i32) - }; - let context = { - let gl_attr = gl_attr.clone().map_sharing(|c| &**c); - let native_display = NativeDisplay::Wayland(Some(display_ptr as *const _)); - EglContext::new(pf_reqs, &gl_attr, native_display, EglSurfaceType::Window, |c, _| { - Ok(c[0]) - }) - .and_then(|p| p.finish(egl_surface.ptr() as *const _))? - }; - let context = Context::Windowed(context, EglSurface(Arc::new(egl_surface))); - Ok(context) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - (**self).make_current() - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - (**self).make_not_current() - } - - #[inline] - pub fn is_current(&self) -> bool { - (**self).is_current() - } - - #[inline] - pub fn get_api(&self) -> crate::Api { - (**self).get_api() - } - - #[inline] - pub unsafe fn raw_handle(&self) -> ffi::EGLContext { - (**self).raw_handle() - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - Some((**self).get_egl_display()) - } - - #[inline] - pub fn resize(&self, width: u32, height: u32) { - match self { - Context::Windowed(_, surface) => surface.0.resize(width as i32, height as i32, 0, 0), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - (**self).get_proc_address(addr) - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - (**self).swap_buffers() - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - (**self).swap_buffers_with_damage(rects) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - (**self).swap_buffers_with_damage_supported() - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - (**self).get_pixel_format() - } -} diff --git a/glutin/src/platform_impl/unix/x11.rs b/glutin/src/platform_impl/unix/x11.rs deleted file mode 100644 index e85757f3a0..0000000000 --- a/glutin/src/platform_impl/unix/x11.rs +++ /dev/null @@ -1,670 +0,0 @@ -#![cfg(feature = "x11")] - -use crate::api::egl::{ - self, Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType, EGL, -}; -use crate::api::glx::{Context as GlxContext, GLX}; -use crate::platform::unix::x11::XConnection; -use crate::platform::unix::{EventLoopWindowTargetExtUnix, WindowBuilderExtUnix, WindowExtUnix}; -use crate::platform_impl::x11_utils; -use crate::{ - Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, - PixelFormatRequirements, Rect, -}; - -use glutin_glx_sys as ffi; - -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -use std::ops::{Deref, DerefMut}; -use std::os::raw; -use std::sync::Arc; - -pub mod utils; - -#[derive(Debug)] -struct NoX11Connection; - -impl std::error::Error for NoX11Connection {} - -impl std::fmt::Display for NoX11Connection { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("failed to get x11 connection") - } -} - -#[derive(Debug)] -pub enum X11Context { - Glx(GlxContext), - Egl(EglContext), -} - -#[derive(Debug)] -pub struct ContextInner { - context: X11Context, -} - -enum Prototype<'a> { - Glx(crate::api::glx::ContextPrototype<'a>), - Egl(crate::api::egl::ContextPrototype<'a>), -} - -#[derive(Debug)] -pub enum Context { - Surfaceless(ContextInner), - PBuffer(ContextInner), - Windowed(ContextInner), -} - -impl Deref for Context { - type Target = ContextInner; - - fn deref(&self) -> &Self::Target { - match self { - Context::Surfaceless(ctx) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Windowed(ctx) => ctx, - } - } -} - -impl DerefMut for Context { - fn deref_mut(&mut self) -> &mut ContextInner { - match self { - Context::Surfaceless(ctx) => ctx, - Context::PBuffer(ctx) => ctx, - Context::Windowed(ctx) => ctx, - } - } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -// FIXME: -// When using egl, all the configs will not support transparency, even if -// transparency does work with glx. -// -// https://bugs.freedesktop.org/show_bug.cgi?id=67676 -// I'm on a patch. -pub fn select_config( - xconn: &Arc, - transparent: Option, - pf_reqs: &PixelFormatRequirements, - config_ids: Vec, - mut convert_to_xvisualinfo: F, -) -> Result<(T, ffi::XVisualInfo), ()> -where - F: FnMut(&T) -> Option, -{ - use crate::platform_impl::x11_utils::Lacks; - let mut chosen_config_id = None; - let mut lacks_what = None; - - for config_id in config_ids { - let visual_infos = match convert_to_xvisualinfo(&config_id) { - Some(vi) => vi, - None => continue, - }; - - let this_lacks_what = x11_utils::examine_visual_info( - xconn, - visual_infos, - transparent == Some(true), - pf_reqs.x11_visual_xid, - ); - - match (lacks_what, &this_lacks_what) { - (Some(Ok(())), _) => unreachable!(), - - // Found it. - (_, Ok(())) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - break; - } - - // Better have something than nothing. - (None, _) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - } - - // Stick with the earlier. - (Some(Err(Lacks::Transparency)), Err(Lacks::Transparency)) => (), - (Some(Err(_)), Err(Lacks::Xid)) => (), - - // Lacking transparency is better than lacking the xid. - (Some(Err(Lacks::Xid)), Err(Lacks::Transparency)) => { - chosen_config_id = Some((config_id, visual_infos)); - lacks_what = Some(this_lacks_what); - } - } - } - - match lacks_what { - Some(Ok(())) => (), - Some(Err(Lacks::Transparency)) => log::warn!( - "Glutin could not a find fb config with an alpha mask. Transparency may be broken." - ), - Some(Err(Lacks::Xid)) => panic!(), - None => unreachable!(), - } - - chosen_config_id.ok_or(()) -} - -impl Context { - fn try_then_fallback(mut f: F) -> Result - where - F: FnMut(bool) -> Result, - { - match f(false) { - Ok(ok) => Ok(ok), - Err(err1) => match f(true) { - Ok(ok) => Ok(ok), - Err(err2) => Err(err1.append(err2)), - }, - } - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - ) -> Result { - Self::try_then_fallback(|fallback| { - Self::new_headless_impl(el, pf_reqs, gl_attr, size, fallback) - }) - } - - fn new_headless_impl( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: Option>, - fallback: bool, - ) -> Result { - let xconn = match el.xlib_xconnection() { - Some(xconn) => xconn, - None => { - return Err(CreationError::NoBackendAvailable(Box::new(NoX11Connection))); - } - }; - - // Get the screen_id for the window being built. - let screen_id = unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }; - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - if let Some(size) = size { - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::PBuffer, - fallback, - fallback, - Some(false), - )?; - - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish_pbuffer(size)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish_pbuffer(size)?), - }; - - let context = Context::PBuffer(ContextInner { context }); - - Ok(context) - } else { - // Surfaceless - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Surfaceless, - !fallback, - fallback, - Some(false), - )?; - - // finish creating the OpenGL context - let context = match context { - // TODO: glx impl - // - // According to GLX_EXT_no_config_context - // > 2) Are no-config contexts constrained to those GL & ES - // > implementations which can support them? - // > - // > RESOLVED: Yes. ES2 + OES_surfaceless_context, ES 3.0, and - // > GL 3.0 all support binding a context without a drawable. - // > This implies that they don't need to know drawable - // > attributes at context creation time. - // > - // > In principle, equivalent functionality could be possible - // > with ES 1.x + OES_surfaceless_context. This extension - // > makes no promises about that. An implementation wishing to - // > reliably support this combination, or a similarly - // > permissive combination for GL < 3.0, should indicate so - // > with an additional GLX extension. - - // Prototype::Glx(ctx) => - // X11Context::Glx(ctx.finish_surfaceless(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish_surfaceless()?), - _ => { - return Err(CreationError::NotSupported( - "Surfaceless GLX context not implemented".to_string(), - )) - } - }; - - let context = Context::Surfaceless(ContextInner { context }); - - Ok(context) - } - } - - #[inline] - fn new_first_stage<'a>( - xconn: &Arc, - pf_reqs: &PixelFormatRequirements, - gl_attr: &'a GlAttributes<&'a Context>, - screen_id: raw::c_int, - builder_glx_u: &'a mut Option>, - builder_egl_u: &'a mut Option>, - surface_type: EglSurfaceType, - prefer_egl: bool, - force_prefer_unless_only: bool, - transparent: Option, - ) -> Result, CreationError> { - let select_config = |cs, display| { - select_config(xconn, transparent, pf_reqs, cs, |config_id| { - let xid = egl::get_native_visual_id(display, *config_id) as ffi::VisualID; - if xid == 0 { - return None; - } - Some(x11_utils::get_visual_info_from_xid(xconn, xid)) - }) - .map(|(c, _)| c) - }; - Ok(match gl_attr.version { - GlRequest::Latest - | GlRequest::Specific(Api::OpenGl, _) - | GlRequest::GlThenGles { .. } => { - // GLX should be preferred over EGL, otherwise crashes may occur - // on X11 – issue #314 - // - // However, with surfaceless, GLX isn't really there, so we - // should prefer EGL. - let glx = |builder_u: &'a mut Option<_>| { - let builder = gl_attr.clone(); - *builder_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Glx(ref c) => c, - _ => panic!("context already exists but is wrong type"), - })); - Ok(Prototype::Glx(GlxContext::new( - Arc::clone(xconn), - pf_reqs, - builder_u.as_ref().unwrap(), - screen_id, - surface_type, - transparent, - )?)) - }; - - let egl = |builder_u: &'a mut Option<_>| { - let builder = gl_attr.clone(); - *builder_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Egl(ref c) => c, - _ => panic!("context already exists but is wrong type"), - })); - let native_display = NativeDisplay::X11(Some(xconn.display as *const _)); - Ok(Prototype::Egl(EglContext::new( - pf_reqs, - builder_u.as_ref().unwrap(), - native_display, - surface_type, - select_config, - )?)) - }; - - // if there is already a context, just use that. - // this prevents the "context already exists but is wrong type" panics above. - if let Some(c) = gl_attr.sharing { - match c.context { - X11Context::Glx(_) => { - GLX.as_ref().expect("found GLX context but GLX not loaded"); - return glx(builder_glx_u); - } - X11Context::Egl(_) => { - EGL.as_ref().expect("found EGL context but EGL not loaded"); - return egl(builder_egl_u); - } - } - } - - // force_prefer_unless_only does what it says on the tin, it - // forces only the preferred method to happen unless it's the - // only method available. - // - // Users of this function should first call with `prefer_egl` - // as ``, with - // `force_prefer_unless_only` as [`false`]. - // - // Then, if those users want to fallback and try the other - // method, they should call us with `prefer_egl` equal to - // `!` and `force_prefer_unless_only` - // as true. - // - // That way, they'll try their fallback if available, unless - // it was their only option and they have already tried it. - if !force_prefer_unless_only { - // If the preferred choice works, don't spend time testing - // if the other works. - if prefer_egl { - if EGL.is_some() { - return egl(builder_egl_u); - } else if GLX.is_some() { - return glx(builder_glx_u); - } - } else if GLX.is_some() { - return glx(builder_glx_u); - } else if EGL.is_some() { - return egl(builder_egl_u); - } - - return Err(CreationError::NotSupported( - "both libGL and libEGL are not present".to_string(), - )); - } else { - if prefer_egl { - if EGL.is_some() { - return egl(builder_egl_u); - } - } else if GLX.is_some() { - return glx(builder_glx_u); - } - - return Err(CreationError::NotSupported( - "lacking either libGL or libEGL so could not fallback to other".to_string(), - )); - } - } - GlRequest::Specific(Api::OpenGlEs, _) => { - if EGL.is_some() { - let builder = gl_attr.clone(); - *builder_egl_u = Some(builder.map_sharing(|c| match c.context { - X11Context::Egl(ref c) => c, - _ => panic!(), - })); - Prototype::Egl(EglContext::new( - pf_reqs, - builder_egl_u.as_ref().unwrap(), - NativeDisplay::X11(Some(xconn.display as *const _)), - surface_type, - select_config, - )?) - } else { - return Err(CreationError::NotSupported("libEGL not present".to_string())); - } - } - GlRequest::Specific(_, _) => { - return Err(CreationError::NotSupported( - "requested specific without gl or gles".to_string(), - )); - } - }) - } - - #[inline] - pub fn new( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result<(Window, Self), CreationError> { - Self::try_then_fallback(|fallback| { - Self::new_impl(wb.clone(), el, pf_reqs, gl_attr, fallback) - }) - } - - fn new_impl( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - fallback: bool, - ) -> Result<(Window, Self), CreationError> { - let xconn = match el.xlib_xconnection() { - Some(xconn) => xconn, - None => { - return Err(CreationError::NoBackendAvailable(Box::new(NoX11Connection))); - } - }; - - // Get the screen_id for the window being built. - let screen_id = unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }; - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - let context = Self::new_first_stage( - &xconn, - pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Window, - fallback, - fallback, - Some(wb.transparent()), - )?; - - // getting the `visual_infos` (a struct that contains information about - // the visual to use) - let visual_infos = match context { - Prototype::Glx(ref p) => *p.get_visual_infos(), - Prototype::Egl(ref p) => { - utils::get_visual_info_from_xid(&xconn, p.get_native_visual_id() as ffi::VisualID) - } - }; - - let win = - wb.with_x11_visual(&visual_infos as *const _).with_x11_screen(screen_id).build(el)?; - - let xwin = win.xlib_window().unwrap(); - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish(xwin as _)?), - }; - - let context = Context::Windowed(ContextInner { context }); - - Ok((win, context)) - } - - #[inline] - pub fn new_raw_context( - xconn: Arc, - xwin: raw::c_ulong, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - ) -> Result { - Self::try_then_fallback(|fallback| { - Self::new_raw_context_impl(&xconn, xwin, pf_reqs, gl_attr, fallback) - }) - } - - fn new_raw_context_impl( - xconn: &Arc, - xwin: raw::c_ulong, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - fallback: bool, - ) -> Result { - let attrs = unsafe { - let mut attrs = ::std::mem::zeroed(); - (xconn.xlib.XGetWindowAttributes)(xconn.display, xwin, &mut attrs); - attrs - }; - - // Not particularly efficient, but it's the only method I can find. - let mut screen_id = 0; - unsafe { - while attrs.screen != (xconn.xlib.XScreenOfDisplay)(xconn.display, screen_id) { - screen_id += 1; - } - } - - let attrs = { - let mut attrs = unsafe { std::mem::zeroed() }; - unsafe { - (xconn.xlib.XGetWindowAttributes)(xconn.display, xwin, &mut attrs); - } - attrs - }; - - let visual_xid = unsafe { (xconn.xlib.XVisualIDFromVisual)(attrs.visual) }; - let mut pf_reqs = pf_reqs.clone(); - pf_reqs.x11_visual_xid = Some(visual_xid); - pf_reqs.depth_bits = Some(attrs.depth as _); - - let mut builder_glx_u = None; - let mut builder_egl_u = None; - - // start the context building process - let context = Self::new_first_stage( - xconn, - &pf_reqs, - gl_attr, - screen_id, - &mut builder_glx_u, - &mut builder_egl_u, - EglSurfaceType::Window, - fallback, - fallback, - None, - )?; - - // finish creating the OpenGL context - let context = match context { - Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xwin)?), - Prototype::Egl(ctx) => X11Context::Egl(ctx.finish(xwin as _)?), - }; - - let context = Context::Windowed(ContextInner { context }); - - Ok(context) - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.make_current(), - X11Context::Egl(ref ctx) => ctx.make_current(), - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.make_not_current(), - X11Context::Egl(ref ctx) => ctx.make_not_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match self.context { - X11Context::Glx(ref ctx) => ctx.is_current(), - X11Context::Egl(ref ctx) => ctx.is_current(), - } - } - - #[inline] - pub fn get_api(&self) -> Api { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_api(), - X11Context::Egl(ref ctx) => ctx.get_api(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> &X11Context { - &self.context - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match self.context { - X11Context::Egl(ref ctx) => Some(ctx.get_egl_display()), - _ => None, - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_proc_address(addr), - X11Context::Egl(ref ctx) => ctx.get_proc_address(addr), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match self.context { - X11Context::Glx(ref ctx) => ctx.buffer_age(), - X11Context::Egl(ref ctx) => ctx.buffer_age(), - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(ref ctx) => ctx.swap_buffers(), - X11Context::Egl(ref ctx) => ctx.swap_buffers(), - } - } - - #[inline] - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - match self.context { - X11Context::Glx(_) => { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - X11Context::Egl(ref ctx) => ctx.swap_buffers_with_damage(rects), - } - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - match self.context { - X11Context::Glx(_) => false, - X11Context::Egl(ref ctx) => ctx.swap_buffers_with_damage_supported(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match self.context { - X11Context::Glx(ref ctx) => ctx.get_pixel_format(), - X11Context::Egl(ref ctx) => ctx.get_pixel_format(), - } - } -} diff --git a/glutin/src/platform_impl/unix/x11/utils.rs b/glutin/src/platform_impl/unix/x11/utils.rs deleted file mode 100644 index 6fd7e2991c..0000000000 --- a/glutin/src/platform_impl/unix/x11/utils.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::platform::unix::x11::XConnection; -use glutin_glx_sys as ffi; - -use std::sync::Arc; - -pub fn get_visual_info_from_xid(xconn: &Arc, xid: ffi::VisualID) -> ffi::XVisualInfo { - assert_ne!(xid, 0); - let mut template: ffi::XVisualInfo = unsafe { std::mem::zeroed() }; - template.visualid = xid; - - let mut num_visuals = 0; - let vi = unsafe { - (xconn.xlib.XGetVisualInfo)( - xconn.display, - ffi::VisualIDMask, - &mut template, - &mut num_visuals, - ) - }; - xconn.check_errors().expect("Failed to call `XGetVisualInfo`"); - assert!(!vi.is_null()); - assert!(num_visuals == 1); - - let vi_copy = unsafe { std::ptr::read(vi as *const _) }; - unsafe { - (xconn.xlib.XFree)(vi as *mut _); - } - vi_copy -} - -#[derive(Clone, Copy, Debug)] -pub enum Lacks { - Transparency, - Xid, -} - -/// Should always check for lack of xid before lack of transparency. -pub fn examine_visual_info( - xconn: &Arc, - visual_infos: ffi::XVisualInfo, - want_transparency: bool, - want_xid: Option, -) -> Result<(), Lacks> { - if let Some(want_xid) = want_xid { - if visual_infos.visualid != want_xid { - return Err(Lacks::Xid); - } - } - - unsafe { - if want_transparency { - let pict_format = (xconn.xrender.XRenderFindVisualFormat)( - xconn.display as *mut _, - visual_infos.visual, - ); - if pict_format.is_null() { - return Err(Lacks::Transparency); - } - - if (*pict_format).direct.alphaMask == 0 { - return Err(Lacks::Transparency); - } - } - } - - Ok(()) -} - -pub use super::select_config; -pub use crate::api::egl::SurfaceType; diff --git a/glutin/src/platform_impl/windows/mod.rs b/glutin/src/platform_impl/windows/mod.rs deleted file mode 100644 index 72a2e568cc..0000000000 --- a/glutin/src/platform_impl/windows/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -#![cfg(target_os = "windows")] - -use crate::{ - Api, ContextCurrentState, ContextError, CreationError, GlAttributes, GlRequest, NotCurrent, - PixelFormat, PixelFormatRequirements, Rect, -}; - -use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType, EGL}; -use crate::api::wgl::Context as WglContext; -use crate::platform::windows::WindowExtWindows; - -use glutin_egl_sys as ffi; -use winapi::shared::windef::{HGLRC, HWND}; - -use winit::dpi; -use winit::event_loop::EventLoopWindowTarget; -use winit::platform::windows::WindowBuilderExtWindows; -use winit::window::{Window, WindowBuilder}; - -use std::marker::PhantomData; -use std::os::raw; - -/// Context handles available on Windows. -#[derive(Clone, Debug)] -pub enum RawHandle { - Egl(ffi::EGLContext), - Wgl(HGLRC), -} - -#[derive(Debug)] -pub enum Context { - /// A regular window - Egl(EglContext), - Wgl(WglContext), - /// A regular window, but invisible. - HiddenWindowEgl(Window, EglContext), - HiddenWindowWgl(Window, WglContext), - /// An EGL pbuffer. - EglPbuffer(EglContext), -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Context { - /// See the docs in the crate root file. - #[inline] - pub fn new_windowed( - wb: WindowBuilder, - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result<(Window, Self), CreationError> { - let win = wb.build(el)?; - let hwnd = win.hwnd() as HWND; - let ctx = Self::new_raw_context(hwnd, pf_reqs, gl_attr)?; - - Ok((win, ctx)) - } - - #[inline] - pub fn new_raw_context( - hwnd: HWND, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - ) -> Result { - match gl_attr.version { - GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => { - match (gl_attr.sharing, &*EGL) { - // We must use WGL. - (Some(&Context::HiddenWindowWgl(_, _)), _) - | (Some(&Context::Wgl(_)), _) - | (None, None) => { - let gl_attr_wgl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::HiddenWindowWgl(_, ref c) | Context::Wgl(ref c) => { - c.get_hglrc() - } - _ => unreachable!(), - }); - unsafe { WglContext::new(pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) } - } - // We must use EGL. - (Some(_), Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Egl(ref c) - | Context::EglPbuffer(ref c) - | Context::HiddenWindowEgl(_, ref c) => c, - _ => unreachable!(), - }); - - EglContext::new( - pf_reqs, - &gl_attr_egl, - NativeDisplay::Other(Some(std::ptr::null())), - EglSurfaceType::Window, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish(hwnd)) - .map(Context::Egl) - } - // Try EGL, fallback to WGL. - (None, Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|_| unreachable!()); - let gl_attr_wgl = gl_attr.clone().map_sharing(|_| unreachable!()); - - if let Ok(c) = EglContext::new( - pf_reqs, - &gl_attr_egl, - NativeDisplay::Other(Some(std::ptr::null())), - EglSurfaceType::Window, - |c, _| Ok(c[0]), - ) - .and_then(|p| p.finish(hwnd)) - { - Ok(Context::Egl(c)) - } else { - unsafe { - WglContext::new(pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) - } - } - } - _ => panic!(), - } - } - _ => { - let gl_attr_wgl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::HiddenWindowWgl(_, ref c) | Context::Wgl(ref c) => c.get_hglrc(), - _ => panic!(), - }); - unsafe { WglContext::new(pf_reqs, &gl_attr_wgl, hwnd).map(Context::Wgl) } - } - } - } - - #[inline] - pub fn new_headless( - el: &EventLoopWindowTarget, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - size: dpi::PhysicalSize, - ) -> Result { - // if EGL is available, we try using EGL first - // if EGL returns an error, we try the hidden window method - match (gl_attr.sharing, &*EGL) { - (None, Some(_)) - | (Some(&Context::Egl(_)), Some(_)) - | (Some(&Context::HiddenWindowEgl(_, _)), Some(_)) - | (Some(&Context::EglPbuffer(_)), Some(_)) => { - let gl_attr_egl = gl_attr.clone().map_sharing(|ctx| match *ctx { - Context::Egl(ref c) - | Context::EglPbuffer(ref c) - | Context::HiddenWindowEgl(_, ref c) => c, - _ => unreachable!(), - }); - - let native_display = NativeDisplay::Other(None); - let context = EglContext::new( - pf_reqs, - &gl_attr_egl, - native_display, - EglSurfaceType::PBuffer, - |c, _| Ok(c[0]), - ) - .and_then(|prototype| prototype.finish_pbuffer(size)) - .map(Context::EglPbuffer); - - if let Ok(context) = context { - return Ok(context); - } - } - _ => (), - } - - let wb = WindowBuilder::new() - .with_visible(false) - .with_inner_size(size) - .with_drag_and_drop(false); - Self::new_windowed(wb, el, pf_reqs, gl_attr).map(|(win, context)| match context { - Context::Egl(context) => Context::HiddenWindowEgl(win, context), - Context::Wgl(context) => Context::HiddenWindowWgl(win, context), - _ => unreachable!(), - }) - } - - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // Method is for API consistency. - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.make_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.make_current(), - } - } - - #[inline] - pub unsafe fn make_not_current(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.make_not_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.make_not_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.is_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.is_current(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_proc_address(addr), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_proc_address(addr), - } - } - - #[inline] - pub fn buffer_age(&self) -> u32 { - match *self { - Context::Egl(ref c) => c.buffer_age(), - _ => 0, - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) => c.swap_buffers(), - Context::Egl(ref c) => c.swap_buffers(), - _ => unreachable!(), - } - } - - #[inline] - pub fn swap_buffers_with_damage(&self, _rects: &[Rect]) -> Result<(), ContextError> { - Err(ContextError::OsError("buffer damage not suported".to_string())) - } - - #[inline] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - false - } - - #[inline] - pub fn get_api(&self) -> Api { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_api(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_api(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - Context::Wgl(ref c) => c.get_pixel_format(), - Context::Egl(ref c) => c.get_pixel_format(), - _ => unreachable!(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { - RawHandle::Wgl(c.get_hglrc()) - } - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()), - } - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match *self { - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => Some(c.get_egl_display()), - _ => None, - } - } -} - -pub trait RawContextExt { - /// Creates a raw context on the provided window. - /// - /// Unsafe behaviour might happen if you: - /// - Provide us with invalid parameters. - /// - The window is destroyed before the context - unsafe fn build_raw_context( - self, - hwnd: isize, - ) -> Result, CreationError> - where - Self: Sized; -} - -impl<'a, T: ContextCurrentState> RawContextExt for crate::ContextBuilder<'a, T> { - #[inline] - unsafe fn build_raw_context( - self, - hwnd: isize, - ) -> Result, CreationError> - where - Self: Sized, - { - let crate::ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - Context::new_raw_context(hwnd as *mut _, &pf_reqs, &gl_attr) - .map(|context| crate::Context { context, phantom: PhantomData }) - .map(|context| crate::RawContext { context, window: () }) - } -} diff --git a/glutin/src/prelude.rs b/glutin/src/prelude.rs new file mode 100644 index 0000000000..23d7244d0f --- /dev/null +++ b/glutin/src/prelude.rs @@ -0,0 +1,17 @@ +//! The glutin prelude. +//! +//! The purpose of this module is to bring common imports, given that all +//! graphics api are on the traits for the documetation sharing purposes. +//! +//! ```no_run +//! # #![allow(unused_imports)] +//! use glutin::prelude::*; +//! ``` + +pub use crate::config::GlConfig; +pub use crate::context::{ + NotCurrentGlContext, NotCurrentGlContextSurfaceAccessor, + PossiblyCurrentContextGlSurfaceAccessor, PossiblyCurrentGlContext, +}; +pub use crate::display::GlDisplay; +pub use crate::surface::GlSurface; diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs new file mode 100644 index 0000000000..21a93b0308 --- /dev/null +++ b/glutin/src/surface.rs @@ -0,0 +1,501 @@ +//! A cross platform GL 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. + fn buffer_age(&self) -> u32; + + /// The width of the underlying surface. + fn width(&self) -> Option; + + /// The height of the underlying surface. + fn height(&self) -> Option; + + /// Check whether the surface is single buffered. + fn is_single_buffered(&self) -> bool; + + /// Swaps the underlying back buffers when the 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 the docs for [`crate::surface::SwapInterval`] on the details. + fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()>; + + /// Resize the surface to the new size. + /// + /// This call is for compatibility reasons, on most platforms it's a no-op. + /// + /// # Platform specific + /// + /// **Wayland:** - resizes the surface. + /// **macOS:** - resizes the context to fit the surface. + /// **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)] +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, since the rest are using 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, since the rest are using it on the context. + pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { + self.attributes.single_buffer = single_buffer; + self + } + + /// 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. +/// +/// The GL surface is not thread safe, it can neither be [`Send`] nor [`Sync`], +/// so it should be created on the thread it'll be used to render. +/// +/// ```compile_fail +/// 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. +#[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), + + /// HWND + #[cfg(wgl_backend)] + Wgl(*const std::ffi::c_void), + + /// Pointer to `NSView`. + #[cfg(cgl_backend)] + Cgl(*const std::ffi::c_void), +} diff --git a/glutin/src/windowed.rs b/glutin/src/windowed.rs deleted file mode 100644 index f400288e96..0000000000 --- a/glutin/src/windowed.rs +++ /dev/null @@ -1,323 +0,0 @@ -use super::*; - -use std::marker::PhantomData; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; - -/// Represents an OpenGL [`Context`] and the [`Window`] with which it is -/// associated. -/// -/// Please see [`ContextWrapper`]. -/// -/// # Example -/// -/// ```no_run -/// # fn main() { -/// let mut el = glutin::event_loop::EventLoop::new(); -/// let wb = glutin::window::WindowBuilder::new(); -/// let windowed_context = glutin::ContextBuilder::new() -/// .build_windowed(wb, &el) -/// .unwrap(); -/// -/// let windowed_context = unsafe { windowed_context.make_current().unwrap() }; -/// # } -/// ``` -pub type WindowedContext = ContextWrapper; - -/// Represents an OpenGL [`Context`] which has an underlying window that is -/// stored separately. -/// -/// This type can only be created via one of three ways: -/// -/// * [`platform::unix::RawContextExt`] -/// * [`platform::windows::RawContextExt`] -/// * [`WindowedContext::split()`] -/// -/// Please see [`ContextWrapper`]. -/// -#[cfg_attr( - target_os = "windows", - doc = "\ -[`platform::windows::RawContextExt`]: crate::platform::windows::RawContextExt -" -)] -#[cfg_attr( - not(target_os = "windows",), - doc = "\ -[`platform::windows::RawContextExt`]: crate::platform -" -)] -#[cfg_attr( - not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - )), - doc = "\ -[`platform::unix::RawContextExt`]: crate::platform -" -)] -#[cfg_attr( - any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ), - doc = "\ -[`platform::unix::RawContextExt`]: crate::platform::unix::RawContextExt -" -)] -pub type RawContext = ContextWrapper; - -/// A context which has an underlying window, which may or may not be stored -/// separately. -/// -/// If the window is stored separately, it is a [`RawContext`]. Otherwise, -/// it is a [`WindowedContext`]. -#[derive(Debug)] -pub struct ContextWrapper { - pub(crate) context: Context, - pub(crate) window: W, -} - -impl WindowedContext { - /// Borrow the inner `W`. - pub fn window(&self) -> &Window { - &self.window - } - - /// Split the [`Window`] apart from the OpenGL [`Context`]. Should only be - /// used when intending to transfer the [`RawContext`] to another - /// thread. - /// - /// Unsaftey: - /// - The OpenGL [`Context`] must be dropped before the [`Window`]. - pub unsafe fn split(self) -> (RawContext, Window) { - (RawContext { context: self.context, window: () }, self.window) - } -} - -impl ContextWrapper { - /// Swaps the buffers in case of double or triple buffering. - /// - /// You should call this function every time you have finished rendering, or - /// the image may not be displayed on the screen. - /// - /// **Warning**: if you enabled vsync, this function will block until the - /// next time the screen is refreshed. However drivers can choose to - /// override your vsync settings, which means that you can't know in - /// advance whether `swap_buffers()` will block or not. - pub fn swap_buffers(&self) -> Result<(), ContextError> { - self.context.context.swap_buffers() - } - - /// Swaps the buffers in case of double or triple buffering using specified - /// damage rects. - /// - /// You should call this function every time you have finished rendering, or - /// the image may not be displayed on the screen. - /// - /// **Warning**: if you enabled vsync, this function will block until the - /// next time the screen is refreshed. However drivers can choose to - /// override your vsync settings, which means that you can't know in - /// advance whether `swap_buffers_with_damage()` will block or not. - pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> { - self.context.context.swap_buffers_with_damage(rects) - } - - /// Returns whether or not swap_buffer_with_damage is available. If this - /// function returns false, any call to swap_buffers_with_damage will - /// return an error. - pub fn swap_buffers_with_damage_supported(&self) -> bool { - self.context.context.swap_buffers_with_damage_supported() - } - - /// Returns the pixel format of the main framebuffer of the context. - pub fn get_pixel_format(&self) -> PixelFormat { - self.context.context.get_pixel_format() - } - - /// Resize the context. - /// - /// Some platforms (macOS, Wayland) require being manually updated when - /// their window or surface is resized. - /// - /// The easiest way of doing this is to take every [`WindowEvent::Resized`] - /// that is received and pass its [`dpi::PhysicalSize`] into this function. - /// - /// [`WindowEvent::Resized`]: winit::event::WindowEvent::Resized - pub fn resize(&self, size: dpi::PhysicalSize) { - let (width, height) = size.into(); - self.context.context.resize(width, height); - } - - /// Query the underlying surface back's buffer age. - /// - /// Return `n` is the number of frames elapsed since it was most recently - /// drawn. - pub fn buffer_age(&self) -> u32 { - self.context.context.buffer_age() - } -} - -impl ContextWrapper { - /// Borrow the inner GL [`Context`]. - pub fn context(&self) -> &Context { - &self.context - } - - /// Sets this context as the current context. The previously current context - /// (if any) is no longer current. - /// - /// A failed call to `make_current` might make this, or no context - /// current. It could also keep the previous context current. What happens - /// varies by platform and error. - /// - /// To attempt to recover and get back into a know state, either: - /// - /// * attempt to use [`is_current()`] to find the new current context; or - /// * call [`make_not_current()`] on both the previously current context - /// and this context. - /// - /// # A higher level overview. - /// - /// In OpenGl, only a single context can be current in a thread at a time. - /// Making a new context current will make the old one not current. - /// Contexts can only be sent to different threads if they are not current. - /// - /// If you call [`make_current()`] on some context, you - /// should call [`treat_as_not_current()`] as soon as - /// possible on the previously current context. - /// - /// If you wish to move a currently current context to a different thread, - /// you should do one of two options: - /// - /// * Call [`make_current()`] on another context, then call - /// [`treat_as_not_current()`] on this context. - /// * Call [`make_not_current()`] on this context. - /// - /// If you are aware of what context you intend to make current next, it is - /// preferable for performance reasons to call [`make_current()`] on that - /// context, then call [`treat_as_not_current()`] on this context. - /// - /// If you are not aware of what context you intend to make current next, - /// consider waiting until you do. If you need this context not current - /// immediately (e.g. to transfer it to another thread), then call - /// [`make_not_current()`] on this context. - /// - /// Please avoid calling [`make_not_current()`] on one context only to call - /// [`make_current()`] on another context before and/or after. This hurts - /// performance by requiring glutin to: - /// - /// * Check if this context is current; then - /// * If it is, change the current context from this context to none; then - /// * Change the current context from none to the new context. - /// - /// Instead prefer the method we mentioned above with [`make_current()`] and - /// [`treat_as_not_current()`]. - /// - /// [`is_current()`]: Self::is_current() - /// [`make_not_current()`]: Self::make_not_current() - /// [`make_current()`]: Self::make_current() - /// [`treat_as_not_current()`]: Self::treat_as_not_current() - pub unsafe fn make_current( - self, - ) -> Result, (Self, ContextError)> { - let window = self.window; - match self.context.make_current() { - Ok(context) => Ok(ContextWrapper { window, context }), - Err((context, err)) => Err((ContextWrapper { window, context }, err)), - } - } - - /// If this context is current, makes this context not current. If this - /// context is not current however, this function does nothing. - /// - /// Please see [`make_current()`][Self::make_current()]. - pub unsafe fn make_not_current( - self, - ) -> Result, (Self, ContextError)> { - let window = self.window; - match self.context.make_not_current() { - Ok(context) => Ok(ContextWrapper { window, context }), - Err((context, err)) => Err((ContextWrapper { window, context }, err)), - } - } - - /// Treats this context as not current, even if it is current. We do no - /// checks to confirm that this is actually case. - /// - /// If unsure whether or not this context is current, please use - /// [`make_not_current()`][Self::make_not_current()] which will do nothing if this - /// context is not current. - /// - /// Please see [`make_current()`][Self::make_current()]. - pub unsafe fn treat_as_not_current(self) -> ContextWrapper { - ContextWrapper { context: self.context.treat_as_not_current(), window: self.window } - } - - /// Treats this context as current, even if it is not current. We do no - /// checks to confirm that this is actually case. - /// - /// This function should only be used if you intend to track context - /// currency without the limited aid of glutin, and you wish to store - /// all the [`Context`]s as [`NotCurrent`]. - /// - /// Please see [`make_current()`][Self::make_current()] for the preferred method - /// of handling context currency. - pub unsafe fn treat_as_current(self) -> ContextWrapper { - ContextWrapper { context: self.context.treat_as_current(), window: self.window } - } - - /// Returns true if this context is the current one in this thread. - pub fn is_current(&self) -> bool { - self.context.is_current() - } - - /// Returns the OpenGL API being used. - pub fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl ContextWrapper { - /// Returns the address of an OpenGL function. - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { - self.context.get_proc_address(addr) - } -} - -impl std::ops::Deref for ContextWrapper { - type Target = Context; - fn deref(&self) -> &Self::Target { - &self.context - } -} - -impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { - /// Builds the given window along with the associated GL context, returning - /// the pair as a [`WindowedContext`]. - /// - /// Errors can occur in two scenarios: - /// - If the window could not be created (via permission denied, - /// incompatible system, out of memory, etc.). This should be very rare. - /// - If the OpenGL [`Context`] could not be created. This generally - /// happens - /// because the underlying platform doesn't support a requested feature. - pub fn build_windowed( - self, - wb: WindowBuilder, - el: &EventLoopWindowTarget, - ) -> Result, CreationError> { - let ContextBuilder { pf_reqs, gl_attr } = self; - let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); - platform_impl::Context::new_windowed(wb, el, &pf_reqs, &gl_attr).map(|(window, context)| { - WindowedContext { window, context: Context { context, phantom: PhantomData } } - }) - } -} diff --git a/glutin_egl_sys/Cargo.toml b/glutin_egl_sys/Cargo.toml index 171b75742b..7925d5517b 100644 --- a/glutin_egl_sys/Cargo.toml +++ b/glutin_egl_sys/Cargo.toml @@ -12,11 +12,8 @@ edition = "2018" [build-dependencies] gl_generator = "0.14" -[target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3" +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = "0.36" features = [ - "winnt", - "winuser", - "wingdi", - "libloaderapi", + "Win32_Foundation", ] diff --git a/glutin_egl_sys/build.rs b/glutin_egl_sys/build.rs index 7b222aaa87..79393e9b48 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -19,28 +19,23 @@ fn main() { || target.contains("ios") { let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - let reg = Registry::new( - Api::Egl, - (1, 5), - Profile::Core, - Fallbacks::All, - [ - "EGL_EXT_buffer_age", - "EGL_EXT_create_context_robustness", - "EGL_EXT_platform_base", - "EGL_EXT_platform_device", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_x11", - "EGL_KHR_create_context", - "EGL_KHR_create_context_no_error", - "EGL_KHR_platform_android", - "EGL_KHR_platform_gbm", - "EGL_KHR_platform_wayland", - "EGL_KHR_platform_x11", - "EGL_KHR_swap_buffers_with_damage", - "EGL_MESA_platform_gbm", - ], - ); + let reg = Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ + "EGL_EXT_buffer_age", + "EGL_EXT_create_context_robustness", + "EGL_EXT_pixel_format_float", + "EGL_EXT_platform_base", + "EGL_EXT_platform_device", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_x11", + "EGL_KHR_create_context", + "EGL_KHR_create_context_no_error", + "EGL_KHR_platform_android", + "EGL_KHR_platform_gbm", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_x11", + "EGL_KHR_swap_buffers_with_damage", + "EGL_MESA_platform_gbm", + ]); if target.contains("ios") { reg.write_bindings(gl_generator::StaticStructGenerator, &mut file) diff --git a/glutin_egl_sys/src/lib.rs b/glutin_egl_sys/src/lib.rs index e6eed6da7d..48494bb368 100644 --- a/glutin_egl_sys/src/lib.rs +++ b/glutin_egl_sys/src/lib.rs @@ -7,13 +7,10 @@ target_os = "netbsd", target_os = "openbsd" ))] -#![allow( - clippy::manual_non_exhaustive, - clippy::missing_safety_doc, - clippy::unnecessary_cast, - non_camel_case_types -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(non_camel_case_types)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::manual_non_exhaustive)] +#![allow(clippy::unnecessary_cast)] pub mod egl { pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; @@ -28,10 +25,13 @@ pub mod egl { pub type NativeWindowType = super::EGLNativeWindowType; include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + + // TODO should upstream this. + pub const PLATFORM_XCB_EXT: super::EGLenum = 0x31DC; + pub const PLATFORM_XCB_SCREEN_EXT: super::EGLenum = 0x31DC; } -pub use self::egl::types::EGLContext; -pub use self::egl::types::EGLDisplay; +pub use self::egl::types::{EGLContext, EGLDisplay}; use std::os::raw; @@ -39,11 +39,12 @@ pub type khronos_utime_nanoseconds_t = khronos_uint64_t; pub type khronos_uint64_t = u64; pub type khronos_ssize_t = raw::c_long; pub type EGLint = i32; +pub type EGLenum = raw::c_uint; pub type EGLNativeDisplayType = *const raw::c_void; pub type EGLNativePixmapType = *const raw::c_void; // FIXME: egl_native_pixmap_t instead #[cfg(target_os = "windows")] -pub type EGLNativeWindowType = winapi::shared::windef::HWND; +pub type EGLNativeWindowType = windows_sys::Win32::Foundation::HWND; #[cfg(target_os = "linux")] pub type EGLNativeWindowType = *const raw::c_void; #[cfg(target_os = "android")] diff --git a/glutin_examples/Cargo.toml b/glutin_examples/Cargo.toml index d5841f9cbb..0ea01e7540 100644 --- a/glutin_examples/Cargo.toml +++ b/glutin_examples/Cargo.toml @@ -7,13 +7,22 @@ repository = "https://github.com/rust-windowing/glutin" license = "Apache-2.0" readme = "../README.md" build = "build.rs" -edition = "2018" +edition = "2021" publish = false +[features] +default = ["egl", "glx", "x11", "wayland", "wgl"] +egl = ["glutin/egl"] +glx = ["glutin/glx", "glutin/x11", "winit/x11"] +wgl = ["glutin/wgl"] +x11 = ["glutin/x11", "winit/x11"] +wayland = ["glutin/wayland", "winit/wayland", "winit/wayland-dlopen", "winit/wayland-csd-adwaita-notitle"] + [dependencies] -glutin = { path = "../glutin" } -takeable-option = "0.4" -image = "0.24" +glutin = { path = "../glutin", default-features = false } +winit = { version = "0.27.2", default-features = false } +raw-window-handle = "0.5.0" [build-dependencies] gl_generator = "0.14" +cfg_aliases = "0.1.1" \ No newline at end of file diff --git a/glutin_examples/build.rs b/glutin_examples/build.rs index 77f6fa545e..2089efebbb 100644 --- a/glutin_examples/build.rs +++ b/glutin_examples/build.rs @@ -1,15 +1,40 @@ -use gl_generator::{Api, Fallbacks, Profile, Registry}; use std::env; use std::fs::File; use std::path::PathBuf; +use cfg_aliases::cfg_aliases; +use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; + fn main() { + // XXX this is taken from glutin/build.rs. + + // Setup alias to reduce `cfg` boilerplate. + cfg_aliases! { + // Systems. + android: { target_os = "android" }, + wasm: { target_arch = "wasm32" }, + macos: { target_os = "macos" }, + ios: { target_os = "ios" }, + apple: { any(target_os = "ios", target_os = "macos") }, + free_unix: { all(unix, not(apple), not(android)) }, + + // Native displays. + x11_platform: { all(feature = "x11", free_unix, not(wasm)) }, + wayland_platform: { all(feature = "wayland", free_unix, not(wasm)) }, + + // Backends. + egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm)) }, + glx_backend: { all(feature = "glx", x11_platform, not(wasm)) }, + wgl_backend: { all(feature = "wgl", windows, not(wasm)) }, + cgl_backend: { all(macos, not(wasm)) }, + } + let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); println!("cargo:rerun-if-changed=build.rs"); let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); - Registry::new(Api::Gles2, (3, 3), Profile::Core, Fallbacks::All, []) - .write_bindings(gl_generator::StructGenerator, &mut file) + Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, []) + .write_bindings(GlobalGenerator, &mut file) .unwrap(); } diff --git a/glutin_examples/examples/buffer_age.rs b/glutin_examples/examples/buffer_age.rs deleted file mode 100644 index 1d7df20723..0000000000 --- a/glutin_examples/examples/buffer_age.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); - - let windowed_context = ContextBuilder::new().with_vsync(true).build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(windowed_context.context()); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => (), - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - println!("Buffer age: {}", windowed_context.buffer_age()); - windowed_context.swap_buffers().unwrap(); - windowed_context.window().request_redraw(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/damage.rs b/glutin_examples/examples/damage.rs deleted file mode 100644 index 8479a469b4..0000000000 --- a/glutin_examples/examples/damage.rs +++ /dev/null @@ -1,92 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use glutin::Rect; - -struct Color { - red: f32, - green: f32, - blue: f32, -} - -impl Color { - fn new() -> Color { - Color { red: 1.0, green: 0.5, blue: 0.0 } - } - fn next(&self) -> Color { - Color { - red: if self.red >= 1.0 { 0.0 } else { self.red + 0.01 }, - green: if self.green >= 1.0 { 0.0 } else { self.green + 0.01 }, - blue: if self.blue >= 1.0 { 0.0 } else { self.blue + 0.01 }, - } - } -} - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); - - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - if !windowed_context.swap_buffers_with_damage_supported() { - panic!("Damage not supported!"); - } - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(windowed_context.context()); - - let mut color = Color::new(); - - gl.draw_frame([color.red, color.green, color.blue, 1.0]); - windowed_context.swap_buffers().unwrap(); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => (), - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::CursorMoved { .. } => { - // Select a new color to render, draw and swap buffers. - // - // Note that damage is *intentionally* being misreported - // here to display the effect of damage. All changes must - // be covered by the reported damage, as the compositor is - // free to read more from the buffer than damage was - // reported, such as when windows unhide. - // - // However, here we only damage the lower left corner to - // show that it is (usually) only the damage that gets - // composited to screen. - // - // Panics if damage is not supported due to the unwrap. - color = color.next(); - gl.draw_frame([color.red, color.green, color.blue, 1.0]); - if windowed_context.swap_buffers_with_damage_supported() { - windowed_context - .swap_buffers_with_damage(&[Rect { - x: 0, - y: 0, - height: 100, - width: 100, - }]) - .unwrap(); - } else { - windowed_context.swap_buffers().unwrap(); - } - } - _ => (), - }, - _ => (), - } - }); -} diff --git a/glutin_examples/examples/fullscreen.rs b/glutin_examples/examples/fullscreen.rs deleted file mode 100644 index 7fcaac00aa..0000000000 --- a/glutin_examples/examples/fullscreen.rs +++ /dev/null @@ -1,117 +0,0 @@ -mod support; - -use glutin::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::monitor::{MonitorHandle, VideoMode}; -use glutin::window::{Fullscreen, WindowBuilder}; -use std::io::{stdin, stdout, Write}; - -fn main() { - let el = EventLoop::new(); - - print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - - let fullscreen = Some(match num { - 1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&el))), - 2 => Fullscreen::Borderless(Some(prompt_for_monitor(&el))), - _ => panic!("Please enter a valid number"), - }); - - println!("Press (F) to toggle fullscreen, (D) to toggle window decorations, and (M) to toggle maximized/minimized."); - - let mut is_maximized = false; - let mut decorations = true; - - let wb = WindowBuilder::new().with_title("Hello world!").with_fullscreen(fullscreen.clone()); - let windowed_context = glutin::ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - let gl = support::load(windowed_context.context()); - - el.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(physical_size) => { - windowed_context.resize(physical_size); - } - WindowEvent::KeyboardInput { - input: KeyboardInput { virtual_keycode: Some(virtual_code), state, .. }, - .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, - (VirtualKeyCode::F, ElementState::Pressed) => { - if windowed_context.window().fullscreen().is_some() { - windowed_context.window().set_fullscreen(None); - } else { - windowed_context.window().set_fullscreen(fullscreen.clone()); - } - } - (VirtualKeyCode::S, ElementState::Pressed) => { - println!("window.fullscreen {:?}", windowed_context.window().fullscreen()); - } - (VirtualKeyCode::M, ElementState::Pressed) => { - is_maximized = !is_maximized; - windowed_context.window().set_maximized(is_maximized); - } - (VirtualKeyCode::D, ElementState::Pressed) => { - decorations = !decorations; - windowed_context.window().set_decorations(decorations); - } - _ => (), - }, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - windowed_context.swap_buffers().unwrap(); - } - _ => {} - } - }); -} - -// Enumerate monitors and prompt user to choose one -fn prompt_for_monitor(el: &EventLoop<()>) -> MonitorHandle { - for (num, monitor) in el.available_monitors().enumerate() { - println!("Monitor #{}: {:?}", num, monitor.name()); - } - - print!("Please write the number of the monitor to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - let monitor = el.available_monitors().nth(num).expect("Please enter a valid ID"); - - println!("Using {:?}", monitor.name()); - - monitor -} - -fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode { - for (i, video_mode) in monitor.video_modes().enumerate() { - println!("Video mode #{}: {}", i, video_mode); - } - - print!("Please write the number of the video mode to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - let video_mode = monitor.video_modes().nth(num).expect("Please enter a valid ID"); - - println!("Using {}", video_mode); - - video_mode -} diff --git a/glutin_examples/examples/headless.rs b/glutin_examples/examples/headless.rs deleted file mode 100644 index 316b97a47f..0000000000 --- a/glutin_examples/examples/headless.rs +++ /dev/null @@ -1,151 +0,0 @@ -mod support; - -use glutin::dpi::PhysicalSize; -use glutin::event_loop::EventLoop; -use glutin::{ - Context, ContextBuilder, ContextCurrentState, CreationError, GlProfile, GlRequest, NotCurrent, -}; -use std::path::Path; -use support::gl; - -#[cfg(target_os = "linux")] -fn build_context_surfaceless( - cb: ContextBuilder, - el: &EventLoop<()>, -) -> Result, CreationError> { - use glutin::platform::unix::HeadlessContextExt; - cb.build_surfaceless(el) -} - -fn build_context_headless( - cb: ContextBuilder, - el: &EventLoop<()>, -) -> Result, CreationError> { - let size_one = PhysicalSize::new(1, 1); - cb.build_headless(el, size_one) -} - -#[cfg(target_os = "linux")] -fn build_context_osmesa( - cb: ContextBuilder, -) -> Result, CreationError> { - use glutin::platform::unix::HeadlessContextExt; - let size_one = PhysicalSize::new(1, 1); - cb.build_osmesa(size_one) -} - -#[cfg(target_os = "linux")] -fn build_context( - cb: ContextBuilder, -) -> Result<(Context, EventLoop<()>), [CreationError; 3]> { - // On unix operating systems, you should always try for surfaceless first, - // and if that does not work, headless (pbuffers), and if that too fails, - // finally osmesa. - // - // If willing, you could attempt to use hidden windows instead of os mesa, - // but note that you must handle events for the window that come on the - // events loop. - let el = EventLoop::new(); - - println!("Trying surfaceless"); - let err1 = match build_context_surfaceless(cb.clone(), &el) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - println!("Trying headless"); - let err2 = match build_context_headless(cb.clone(), &el) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - println!("Trying osmesa"); - let err3 = match build_context_osmesa(cb) { - Ok(ctx) => return Ok((ctx, el)), - Err(err) => err, - }; - - Err([err1, err2, err3]) -} - -#[cfg(not(target_os = "linux"))] -fn build_context( - cb: ContextBuilder, -) -> Result<(Context, EventLoop<()>), CreationError> { - let el = EventLoop::new(); - build_context_headless(cb.clone(), &el).map(|ctx| (ctx, el)) -} - -fn main() { - let cb = ContextBuilder::new().with_gl_profile(GlProfile::Core).with_gl(GlRequest::Latest); - let size = PhysicalSize::new(768., 480.); - - let (headless_context, _el) = build_context(cb).unwrap(); - - let headless_context = unsafe { headless_context.make_current().unwrap() }; - - let gl = support::load(&headless_context); - - let mut fb = 0; - let mut render_buf = 0; - unsafe { - // Using the fb backing a pbuffer is very much a bad idea. Fails on - // many platforms, and is deprecated. Better just make your own fb. - // - // Surfaceless doesn't come with a surface, as the name implies, so - // you must make your own fb. - // - // Making an fb is not neccesary with osmesa, however, can't be bothered - // to have a different code path. - gl.gl.GenRenderbuffers(1, &mut render_buf); - gl.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - gl.gl.RenderbufferStorage(gl::RENDERBUFFER, gl::RGB8, size.width as _, size.height as _); - gl.gl.GenFramebuffers(1, &mut fb); - gl.gl.BindFramebuffer(gl::FRAMEBUFFER, fb); - gl.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - - gl.gl.Viewport(0, 0, size.width as _, size.height as _); - } - - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - - let mut pixels: Vec = vec![]; - pixels.resize(3 * size.width as usize * size.height as usize, 0); - unsafe { - gl.gl.ReadPixels( - 0, - 0, - size.width as _, - size.height as _, - gl::RGB, - gl::UNSIGNED_BYTE, - pixels.as_mut_ptr() as *mut _, - ); - } - - let mut pixels_flipped: Vec = vec![]; - for v in (0..size.height as _).rev() { - let s = 3 * v as usize * size.width as usize; - let o = 3 * size.width as usize; - pixels_flipped.extend_from_slice(&pixels[s..(s + o)]); - } - - image::save_buffer( - &Path::new("headless.png"), - &pixels_flipped, - size.width as u32, - size.height as u32, - image::ColorType::Rgb8, - ) - .unwrap(); - - unsafe { - gl.gl.DeleteFramebuffers(1, &fb); - gl.gl.DeleteRenderbuffers(1, &render_buf); - } -} diff --git a/glutin_examples/examples/multiwindow.rs b/glutin_examples/examples/multiwindow.rs deleted file mode 100644 index 0a13168940..0000000000 --- a/glutin_examples/examples/multiwindow.rs +++ /dev/null @@ -1,65 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use support::{ContextCurrentWrapper, ContextTracker, ContextWrapper}; - -fn main() { - let el = EventLoop::new(); - let mut ct = ContextTracker::default(); - - let mut windows = std::collections::HashMap::new(); - for index in 0..3 { - let title = format!("Charming Window #{}", index + 1); - let wb = WindowBuilder::new().with_title(title); - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - let gl = support::load(windowed_context.context()); - let window_id = windowed_context.window().id(); - let context_id = ct.insert(ContextCurrentWrapper::PossiblyCurrent( - ContextWrapper::Windowed(windowed_context), - )); - windows.insert(window_id, (context_id, gl, index)); - } - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, window_id } => match event { - WindowEvent::Resized(physical_size) => { - let windowed_context = ct.get_current(windows[&window_id].0).unwrap(); - let windowed_context = windowed_context.windowed(); - windowed_context.resize(physical_size); - } - WindowEvent::CloseRequested => { - if let Some((cid, _, _)) = windows.remove(&window_id) { - ct.remove(cid); - println!("Window with ID {:?} has been closed", window_id); - } - } - _ => (), - }, - Event::RedrawRequested(window_id) => { - let window = &windows[&window_id]; - - let mut color = [1.0, 0.5, 0.7, 1.0]; - color.swap(0, window.2 % 3); - - let windowed_context = ct.get_current(window.0).unwrap(); - - window.1.draw_frame(color); - windowed_context.windowed().swap_buffers().unwrap(); - } - _ => (), - } - - if windows.is_empty() { - *control_flow = ControlFlow::Exit - } else { - *control_flow = ControlFlow::Wait - } - }); -} diff --git a/glutin_examples/examples/raw_context.rs b/glutin_examples/examples/raw_context.rs deleted file mode 100644 index 367c22c659..0000000000 --- a/glutin_examples/examples/raw_context.rs +++ /dev/null @@ -1,129 +0,0 @@ -#[cfg(any(target_os = "linux", target_os = "windows"))] -mod support; - -fn main() { - #[cfg(not(any(target_os = "linux", target_os = "windows")))] - unimplemented!(); - #[cfg(any(target_os = "linux", target_os = "windows"))] - this_example::main(); -} - -#[cfg(any(target_os = "linux", target_os = "windows"))] -mod this_example { - use super::support; - use glutin::event::{Event, WindowEvent}; - use glutin::event_loop::{ControlFlow, EventLoop}; - use glutin::window::WindowBuilder; - use glutin::ContextBuilder; - use std::io::Write; - use takeable_option::Takeable; - - pub fn main() { - print!("Do you want transparency? (true/false) (default: true): "); - std::io::stdout().flush().unwrap(); - - let mut transparency = String::new(); - std::io::stdin().read_line(&mut transparency).unwrap(); - let transparency = transparency.trim().parse().unwrap_or_else(|_| { - println!("Unknown input, assuming true."); - true - }); - - let (raw_context, el) = { - let el = EventLoop::new(); - let mut wb = WindowBuilder::new().with_title("A fantastic window!"); - - if transparency { - wb = wb.with_decorations(false).with_transparent(true); - } - - #[cfg(target_os = "linux")] - unsafe { - use glutin::platform::unix::{ - EventLoopWindowTargetExtUnix, RawContextExt, WindowExtUnix, - }; - - if el.is_wayland() { - let win = wb.build(&el).unwrap(); - let size = win.inner_size(); - let (width, height): (u32, u32) = size.into(); - - let display_ptr = win.wayland_display().unwrap() as *const _; - let surface = win.wayland_surface().unwrap(); - - let raw_context = ContextBuilder::new() - .build_raw_wayland_context(display_ptr, surface, width, height) - .unwrap(); - - (raw_context, el) - } else { - if transparency { - unimplemented!( - r#" -Users should make sure that the window gets built with an x11 visual that -supports transparency. Winit does not currently do this by default for x11 -because it is not provided with enough details to make a good choice. Normally -glutin decides this for winit, but this is not the case for raw contexts. - -Depending on the default order of the x11 visuals, transparency may by sheer -luck work for you. - -Such a task of selecting the appropriate x11 visual is outside the limited -scope of the glutin examples. Implementing it would likely require a lot of -platform specific egl/glx/x11 calls or exposing a lot of glutin's internals. -File a PR if you are interested in implementing the latter. - "# - ) - } - - let win = wb.build(&el).unwrap(); - let xconn = el.xlib_xconnection().unwrap(); - let xwindow = win.xlib_window().unwrap(); - let raw_context = - ContextBuilder::new().build_raw_x11_context(xconn, xwindow).unwrap(); - - (raw_context, el) - } - } - - #[cfg(target_os = "windows")] - unsafe { - let win = wb.build(&el).unwrap(); - use glutin::platform::windows::{RawContextExt, WindowExtWindows}; - - let hwnd = win.hwnd(); - let raw_context = ContextBuilder::new().build_raw_context(hwnd).unwrap(); - - (raw_context, el) - } - }; - - let raw_context = unsafe { raw_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", raw_context.get_pixel_format()); - - let gl = support::load(&*raw_context); - - let mut raw_context = Takeable::new(raw_context); - el.run(move |event, _, control_flow| { - println!("el {:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => { - Takeable::take(&mut raw_context); // Make sure it drops first - } - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => raw_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame(if transparency { [0.0; 4] } else { [1.0, 0.5, 0.7, 1.0] }); - raw_context.swap_buffers().unwrap(); - } - _ => (), - } - }); - } -} diff --git a/glutin_examples/examples/sharing.rs b/glutin_examples/examples/sharing.rs deleted file mode 100644 index c830aa165a..0000000000 --- a/glutin_examples/examples/sharing.rs +++ /dev/null @@ -1,142 +0,0 @@ -mod support; - -use glutin::dpi::PhysicalSize; -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; -use support::{gl, ContextCurrentWrapper, ContextTracker, ContextWrapper}; - -fn make_renderbuf(gl: &support::Gl, size: PhysicalSize) -> gl::types::GLuint { - let mut render_buf = 0; - unsafe { - gl.gl.GenRenderbuffers(1, &mut render_buf); - gl.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - gl.gl.RenderbufferStorage(gl::RENDERBUFFER, gl::RGB8, size.width as _, size.height as _); - } - - render_buf -} - -fn main() { - let el = EventLoop::new(); - let size = PhysicalSize::new(768, 480); - - let mut ct = ContextTracker::default(); - - let headless_context = - ContextBuilder::new().build_headless(&el, PhysicalSize::new(1, 1)).unwrap(); - - let wb = WindowBuilder::new().with_title("A fantastic window!").with_inner_size(size); - let windowed_context = - ContextBuilder::new().with_shared_lists(&headless_context).build_windowed(wb, &el).unwrap(); - - let headless_id = - ct.insert(ContextCurrentWrapper::NotCurrent(ContextWrapper::Headless(headless_context))); - let windowed_id = - ct.insert(ContextCurrentWrapper::NotCurrent(ContextWrapper::Windowed(windowed_context))); - - let windowed_context = ct.get_current(windowed_id).unwrap(); - println!( - "Pixel format of the window's GL context: {:?}", - windowed_context.windowed().get_pixel_format() - ); - let glw = support::load(windowed_context.windowed().context()); - - let render_buf = make_renderbuf(&glw, size); - - let mut window_fb = 0; - unsafe { - glw.gl.GenFramebuffers(1, &mut window_fb); - // Both `GL_DRAW_FRAMEBUFFER` and `GL_READ_FRAMEBUFFER` need to be - // non-zero for `glFramebufferRenderbuffer`. We can change - // `GL_DRAW_FRAMEBUFFER` after. - glw.gl.BindFramebuffer(gl::FRAMEBUFFER, window_fb); - glw.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - glw.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); - glw.gl.Viewport(0, 0, size.width as _, size.height as _); - } - - let headless_context = ct.get_current(headless_id).unwrap(); - let glc = support::load(headless_context.headless()); - - let mut context_fb = 0; - unsafe { - // Using the fb backing a pbuffer is very much a bad idea. Fails on - // many platforms, and is deprecated. Better just make your own fb. - glc.gl.GenFramebuffers(1, &mut context_fb); - glc.gl.BindFramebuffer(gl::FRAMEBUFFER, context_fb); - glc.gl.BindRenderbuffer(gl::RENDERBUFFER, render_buf); - glc.gl.FramebufferRenderbuffer( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::RENDERBUFFER, - render_buf, - ); - glc.gl.Viewport(0, 0, size.width as _, size.height as _); - } - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => unsafe { - let _ = ct.get_current(windowed_id).unwrap(); - glw.gl.DeleteFramebuffers(1, &window_fb); - glw.gl.DeleteRenderbuffers(1, &render_buf); - let _ = ct.get_current(headless_id).unwrap(); - glc.gl.DeleteFramebuffers(1, &context_fb); - }, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => { - let windowed_context = ct.get_current(windowed_id).unwrap(); - windowed_context.windowed().resize(physical_size); - - unsafe { - windowed_context.windowed().swap_buffers().unwrap(); - glw.gl.RenderbufferStorage( - gl::RENDERBUFFER, - gl::RGB8, - size.width as _, - size.height as _, - ); - glw.gl.Viewport(0, 0, size.width as _, size.height as _); - - let _ = ct.get_current(headless_id).unwrap(); - glc.gl.Viewport(0, 0, size.width as _, size.height as _); - } - } - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - let _ = ct.get_current(headless_id).unwrap(); - glc.draw_frame([1.0, 0.5, 0.7, 1.0]); - - let windowed_context = ct.get_current(windowed_id).unwrap(); - unsafe { - glw.gl.BlitFramebuffer( - 0, - 0, - size.width as _, - size.height as _, - 0, - 0, - size.width as _, - size.height as _, - gl::COLOR_BUFFER_BIT, - gl::NEAREST, - ); - } - windowed_context.windowed().swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/support/mod.rs b/glutin_examples/examples/support/mod.rs index dfc388dd61..fdb1de5acb 100644 --- a/glutin_examples/examples/support/mod.rs +++ b/glutin_examples/examples/support/mod.rs @@ -1,365 +1,91 @@ -use glutin::{self, PossiblyCurrent}; +//! Support module for the glutin examples. +#![allow(dead_code)] +#![allow(unused_variables)] -use std::ffi::CStr; +use std::num::NonZeroU32; -pub mod gl { - #![allow( - clippy::manual_non_exhaustive, - clippy::too_many_arguments, - clippy::unused_unit, - clippy::upper_case_acronyms, - non_camel_case_types - )] +use raw_window_handle::{HasRawWindowHandle, RawDisplayHandle, RawWindowHandle}; + +use winit::event_loop::EventLoop; +#[cfg(glx_backend)] +use winit::platform::unix; +use winit::window::{Window, WindowBuilder}; - pub use self::Gles2 as Gl; +use glutin::config::{Config, ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder}; +use glutin::display::{Display, DisplayPicker}; +use glutin::prelude::*; +use glutin::surface::{Surface, SurfaceAttributes, SurfaceAttributesBuilder, WindowSurface}; - // gl_bindings.rs is generated in build.rs using https://crates.io/crates/gl_generator +pub mod gl { + #![allow(clippy::all)] include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); } -pub struct Gl { - pub gl: gl::Gl, +/// Structure to hold winit window and gl surface. +pub struct GlWindow { + pub surface: Surface, + pub window: Window, } -pub fn load(gl_context: &glutin::Context) -> Gl { - let gl = gl::Gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _); - - let version = unsafe { - let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - }; - - println!("OpenGL version {}", version); - - unsafe { - let vs = gl.CreateShader(gl::VERTEX_SHADER); - gl.ShaderSource(vs, 1, [VS_SRC.as_ptr() as *const _].as_ptr(), std::ptr::null()); - gl.CompileShader(vs); - - let fs = gl.CreateShader(gl::FRAGMENT_SHADER); - gl.ShaderSource(fs, 1, [FS_SRC.as_ptr() as *const _].as_ptr(), std::ptr::null()); - gl.CompileShader(fs); - - let program = gl.CreateProgram(); - gl.AttachShader(program, vs); - gl.AttachShader(program, fs); - gl.LinkProgram(program); - gl.UseProgram(program); - - let mut vb = std::mem::zeroed(); - gl.GenBuffers(1, &mut vb); - gl.BindBuffer(gl::ARRAY_BUFFER, vb); - gl.BufferData( - gl::ARRAY_BUFFER, - (VERTEX_DATA.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, - VERTEX_DATA.as_ptr() as *const _, - gl::STATIC_DRAW, - ); - - if gl.BindVertexArray.is_loaded() { - let mut vao = std::mem::zeroed(); - gl.GenVertexArrays(1, &mut vao); - gl.BindVertexArray(vao); - } - - let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _); - let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _); - gl.VertexAttribPointer( - pos_attrib as gl::types::GLuint, - 2, - gl::FLOAT, - 0, - 5 * std::mem::size_of::() as gl::types::GLsizei, - std::ptr::null(), - ); - gl.VertexAttribPointer( - color_attrib as gl::types::GLuint, - 3, - gl::FLOAT, - 0, - 5 * std::mem::size_of::() as gl::types::GLsizei, - (2 * std::mem::size_of::()) as *const () as *const _, - ); - gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint); - gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint); +impl GlWindow { + pub fn new(event_loop: &EventLoop, display: &Display, config: &Config) -> Self { + let window = WindowBuilder::new().with_transparent(true).build(event_loop).unwrap(); + let attrs = surface_attributes(&window); + let surface = unsafe { display.create_window_surface(config, &attrs).unwrap() }; + Self { window, surface } } - Gl { gl } -} - -impl Gl { - pub fn draw_frame(&self, color: [f32; 4]) { - unsafe { - self.gl.ClearColor(color[0], color[1], color[2], color[3]); - self.gl.Clear(gl::COLOR_BUFFER_BIT); - self.gl.DrawArrays(gl::TRIANGLES, 0, 3); - } + pub fn from_existing(display: &Display, window: Window, config: &Config) -> Self { + let attrs = surface_attributes(&window); + let surface = unsafe { display.create_window_surface(config, &attrs).unwrap() }; + Self { window, surface } } } -#[rustfmt::skip] -static VERTEX_DATA: [f32; 15] = [ - -0.5, -0.5, 1.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 1.0, 0.0, - 0.5, -0.5, 0.0, 0.0, 1.0, -]; - -const VS_SRC: &[u8] = b" -#version 100 -precision mediump float; - -attribute vec2 position; -attribute vec3 color; - -varying vec3 v_color; - -void main() { - gl_Position = vec4(position, 0.0, 1.0); - v_color = color; +/// Create template to find OpenGL config. +pub fn config_template(raw_window_handle: RawWindowHandle) -> ConfigTemplate { + ConfigTemplateBuilder::new() + .with_alpha_size(8) + .with_transparency(true) + .compatible_with_native_window(raw_window_handle) + .with_surface_type(ConfigSurfaceTypes::WINDOW) + .build() } -\0"; - -const FS_SRC: &[u8] = b" -#version 100 -precision mediump float; - -varying vec3 v_color; -void main() { - gl_FragColor = vec4(v_color, 1.0); +/// Create surface attributes for window surface. +pub fn surface_attributes(window: &Window) -> SurfaceAttributes { + let (width, height): (u32, u32) = window.inner_size().into(); + let raw_window_handle = window.raw_window_handle(); + SurfaceAttributesBuilder::::new().build( + raw_window_handle, + NonZeroU32::new(width).unwrap(), + NonZeroU32::new(height).unwrap(), + ) } -\0"; - -pub use self::context_tracker::{ContextCurrentWrapper, ContextId, ContextTracker, ContextWrapper}; - -#[allow(dead_code)] // Not used by all examples -mod context_tracker { - use glutin::{ - self, Context, ContextCurrentState, ContextError, NotCurrent, PossiblyCurrent, - WindowedContext, - }; - use takeable_option::Takeable; - - pub enum ContextWrapper { - Headless(Context), - Windowed(WindowedContext), - } - - impl ContextWrapper { - pub fn headless(&mut self) -> &mut Context { - match self { - ContextWrapper::Headless(ref mut ctx) => ctx, - _ => panic!(), - } - } - - pub fn windowed(&mut self) -> &mut WindowedContext { - match self { - ContextWrapper::Windowed(ref mut ctx) => ctx, - _ => panic!(), - } - } - fn map( - self, - fh: FH, - fw: FW, - ) -> Result, (Self, ContextError)> - where - FH: FnOnce(Context) -> Result, (Context, ContextError)>, - FW: FnOnce( - WindowedContext, - ) - -> Result, (WindowedContext, ContextError)>, - { - match self { - ContextWrapper::Headless(ctx) => match fh(ctx) { - Ok(ctx) => Ok(ContextWrapper::Headless(ctx)), - Err((ctx, err)) => Err((ContextWrapper::Headless(ctx), err)), - }, - ContextWrapper::Windowed(ctx) => match fw(ctx) { - Ok(ctx) => Ok(ContextWrapper::Windowed(ctx)), - Err((ctx, err)) => Err((ContextWrapper::Windowed(ctx), err)), - }, - } - } - } - - pub enum ContextCurrentWrapper { - PossiblyCurrent(ContextWrapper), - NotCurrent(ContextWrapper), - } - - impl ContextCurrentWrapper { - fn map_possibly(self, f: F) -> Result - where - F: FnOnce( - ContextWrapper, - ) -> Result< - ContextWrapper, - (ContextWrapper, ContextError), - >, - { - match self { - ret @ ContextCurrentWrapper::NotCurrent(_) => Ok(ret), - ContextCurrentWrapper::PossiblyCurrent(ctx) => match f(ctx) { - Ok(ctx) => Ok(ContextCurrentWrapper::NotCurrent(ctx)), - Err((ctx, err)) => Err((ContextCurrentWrapper::PossiblyCurrent(ctx), err)), - }, - } - } - - fn map_not(self, f: F) -> Result - where - F: FnOnce( - ContextWrapper, - ) -> Result< - ContextWrapper, - (ContextWrapper, ContextError), - >, - { - match self { - ret @ ContextCurrentWrapper::PossiblyCurrent(_) => Ok(ret), - ContextCurrentWrapper::NotCurrent(ctx) => match f(ctx) { - Ok(ctx) => Ok(ContextCurrentWrapper::PossiblyCurrent(ctx)), - Err((ctx, err)) => Err((ContextCurrentWrapper::NotCurrent(ctx), err)), - }, - } - } - } - - pub type ContextId = usize; - #[derive(Default)] - pub struct ContextTracker { - current: Option, - others: Vec<(ContextId, Takeable)>, - next_id: ContextId, - } - - impl ContextTracker { - pub fn insert(&mut self, ctx: ContextCurrentWrapper) -> ContextId { - let id = self.next_id; - self.next_id += 1; - - if let ContextCurrentWrapper::PossiblyCurrent(_) = ctx { - if let Some(old_current) = self.current { - unsafe { - self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| Ok(ctx.treat_as_not_current()), - |ctx| Ok(ctx.treat_as_not_current()), - ) - }) - }) - .unwrap() - } - } - self.current = Some(id); - } - - self.others.push((id, Takeable::new(ctx))); - id - } - - pub fn remove(&mut self, id: ContextId) -> ContextCurrentWrapper { - if Some(id) == self.current { - self.current.take(); - } - - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - Takeable::take(&mut self.others.remove(this_index).1) - } - - fn modify(&mut self, id: ContextId, f: F) -> Result<(), ContextError> - where - F: FnOnce( - ContextCurrentWrapper, - ) - -> Result, - { - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - - let this_context = Takeable::take(&mut self.others[this_index].1); - - match f(this_context) { - Err((ctx, err)) => { - self.others[this_index].1 = Takeable::new(ctx); - Err(err) - } - Ok(ctx) => { - self.others[this_index].1 = Takeable::new(ctx); - Ok(()) - } - } - } - - pub fn get_current( - &mut self, - id: ContextId, - ) -> Result<&mut ContextWrapper, ContextError> { - unsafe { - let this_index = self.others.binary_search_by(|(sid, _)| sid.cmp(&id)).unwrap(); - if Some(id) != self.current { - let old_current = self.current.take(); - - if let Err(err) = self.modify(id, |ctx| { - ctx.map_not(|ctx| { - ctx.map(|ctx| ctx.make_current(), |ctx| ctx.make_current()) - }) - }) { - // Oh noes, something went wrong - // Let's at least make sure that no context is current. - if let Some(old_current) = old_current { - if let Err(err2) = self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| ctx.make_not_current(), - |ctx| ctx.make_not_current(), - ) - }) - }) { - panic!( - "Could not `make_current` nor `make_not_current`, {:?}, {:?}", - err, err2 - ); - } - } - - if let Err(err2) = self.modify(id, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map(|ctx| ctx.make_not_current(), |ctx| ctx.make_not_current()) - }) - }) { - panic!( - "Could not `make_current` nor `make_not_current`, {:?}, {:?}", - err, err2 - ); - } - - return Err(err); - } - - self.current = Some(id); - - if let Some(old_current) = old_current { - self.modify(old_current, |ctx| { - ctx.map_possibly(|ctx| { - ctx.map( - |ctx| Ok(ctx.treat_as_not_current()), - |ctx| Ok(ctx.treat_as_not_current()), - ) - }) - }) - .unwrap(); - } - } - - match *self.others[this_index].1 { - ContextCurrentWrapper::PossiblyCurrent(ref mut ctx) => Ok(ctx), - ContextCurrentWrapper::NotCurrent(_) => panic!(), - } - } - } - } +/// Create the display. +pub fn create_display( + raw_display: RawDisplayHandle, + raw_window_handle: RawWindowHandle, +) -> Display { + #[cfg(not(all(glx_backend, wgl_backend)))] + let picker = DisplayPicker::new(); + + #[cfg(all(egl_backend, wgl_backend))] + let picker = DisplayPicker::new() + .with_most_compatible_for_window(raw_window_handle) + .with_api_preference(glutin::display::DisplayApiPreference::WglThenEgl); + + #[cfg(all(egl_backend, glx_backend))] + let picker = DisplayPicker::new() + .with_api_preference(glutin::display::DisplayApiPreference::GlxThenEgl) + .with_glx_error_registrar(Box::new(unix::register_xlib_error_hook)); + + #[cfg(all(glx_backend, not(egl_backend)))] + let picker = DisplayPicker::new() + .with_api_preference(glutin::display::DisplayApiPreference::Glx) + .with_glx_error_registrar(Box::new(unix::register_xlib_error_hook)); + + // Create connection to underlying OpenGL client Api. + unsafe { Display::from_raw(raw_display, picker).unwrap() } } diff --git a/glutin_examples/examples/transparent.rs b/glutin_examples/examples/transparent.rs deleted file mode 100644 index df8d26c29a..0000000000 --- a/glutin_examples/examples/transparent.rs +++ /dev/null @@ -1,41 +0,0 @@ -mod support; - -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; - -fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new() - .with_title("A transparent window!") - .with_decorations(false) - .with_transparent(true); - - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); - - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; - - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); - - let gl = support::load(windowed_context.context()); - - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => (), - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([0.0; 4]); - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/glutin_examples/examples/window.rs b/glutin_examples/examples/window.rs index 4af3d1543a..10281a3bfa 100644 --- a/glutin_examples/examples/window.rs +++ b/glutin_examples/examples/window.rs @@ -1,37 +1,97 @@ +use std::ffi::CString; +use std::num::NonZeroU32; + +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{ControlFlow, EventLoop}; +use winit::window::WindowBuilder; + +use glutin::context::ContextAttributesBuilder; +use glutin::prelude::*; +use glutin::surface::SwapInterval; + mod support; -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; -use glutin::window::WindowBuilder; -use glutin::ContextBuilder; +use support::*; fn main() { - let el = EventLoop::new(); - let wb = WindowBuilder::new().with_title("A fantastic window!"); + let event_loop = EventLoop::new(); - let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap(); + let raw_display = event_loop.raw_display_handle(); - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + // We create a window before the display to accomodate for WGL, since it + // requires creating HDC for properly loading the WGL and it should be taken + // from the window you'll be rendering into. + let window = WindowBuilder::new().with_transparent(true).build(&event_loop).unwrap(); + let raw_window_handle = window.raw_window_handle(); - println!("Pixel format of the window's GL context: {:?}", windowed_context.get_pixel_format()); + // Create the GL display. This will create display automatically for the + // underlying GL platform. See support module on how it's being done. + let gl_display = create_display(raw_display, raw_window_handle); - let gl = support::load(windowed_context.context()); + // Create the config we'll be used for window. We'll use the native window + // raw-window-handle for it to get the rigth visual and use proper hdc. Note + // that you can likely use it for other windows using the same config. + let template = config_template(window.raw_window_handle()); + let config = unsafe { gl_display.find_configs(template).unwrap().next().unwrap() }; - el.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; + // Create a wrapper for GL window and surface. + let gl_window = GlWindow::from_existing(&gl_display, window, &config); + + // The context creation part. It can be created before surface and that's how + // it's expected in multithreaded + multiwindow operation mode, since you + // can send NotCurrentContext, but not Surface. + let context_attributes = ContextAttributesBuilder::new().build(Some(raw_window_handle)); + let gl_context = unsafe { gl_display.create_context(&config, &context_attributes).unwrap() }; + + // Make it current and load symbols. + let gl_context = gl_context.make_current(&gl_window.surface).unwrap(); + gl::load_with(|symbol| { + let symbol = CString::new(symbol).unwrap(); + gl_context.get_proc_address(symbol.as_c_str()) as *const _ + }); + + // Try setting vsync. + if let Err(res) = gl_window + .surface + .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) + { + eprintln!("Error setting vsync: {:?}", res); + } + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; match event { - Event::LoopDestroyed => (), Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + if size.width != 0 && size.height != 0 { + // Some platforms like EGL require resizing GL surface to update the size + // Notable platforms here are Wayland and macOS, other don't require it + // and the function is no-op, but it's wise to resize it for portability + // reasons. + gl_window.surface.resize( + &gl_context, + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + } + }, + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + }, _ => (), }, - Event::RedrawRequested(_) => { - gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - windowed_context.swap_buffers().unwrap(); - } + Event::RedrawEventsCleared => { + unsafe { + gl::ClearColor(0., 0.3, 0.3, 0.8); + gl::Clear(gl::COLOR_BUFFER_BIT); + gl_window.window.request_redraw(); + } + + gl_window.surface.swap_buffers(&gl_context).unwrap(); + }, _ => (), } }); diff --git a/glutin_examples/ios-example/.gitignore b/glutin_examples/ios-example/.gitignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/glutin_examples/ios-example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.pbxproj b/glutin_examples/ios-example/GlutinExample.xcodeproj/project.pbxproj deleted file mode 100644 index b5e416f70a..0000000000 --- a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.pbxproj +++ /dev/null @@ -1,343 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 96517E7A23733540004181B9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 96517E7923733540004181B9 /* main.m */; }; - 96517E8323733807004181B9 /* libglutin_ios_example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 96517E8223733807004181B9 /* libglutin_ios_example.a */; }; - 96517E8823733856004181B9 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96517E8723733856004181B9 /* OpenGLES.framework */; }; - 96517E8A23733863004181B9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96517E8923733863004181B9 /* UIKit.framework */; }; - 96517E8C237338A2004181B9 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96517E8B237338A2004181B9 /* Security.framework */; }; - 96517E8F237338C4004181B9 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96517E8523733846004181B9 /* GLKit.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 96517E6423733540004181B9 /* GlutinExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GlutinExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 96517E7823733540004181B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 96517E7923733540004181B9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 96517E81237337FE004181B9 /* glutin_ios_example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glutin_ios_example.h; sourceTree = ""; }; - 96517E8223733807004181B9 /* libglutin_ios_example.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libglutin_ios_example.a; sourceTree = ""; }; - 96517E8523733846004181B9 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; - 96517E8723733856004181B9 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; - 96517E8923733863004181B9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - 96517E8B237338A2004181B9 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - 96517E8D237338BB004181B9 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 96517E6123733540004181B9 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 96517E8323733807004181B9 /* libglutin_ios_example.a in Frameworks */, - 96517E8A23733863004181B9 /* UIKit.framework in Frameworks */, - 96517E8C237338A2004181B9 /* Security.framework in Frameworks */, - 96517E8F237338C4004181B9 /* GLKit.framework in Frameworks */, - 96517E8823733856004181B9 /* OpenGLES.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 96517E5B23733540004181B9 = { - isa = PBXGroup; - children = ( - 96517E8023733582004181B9 /* rust */, - 96517E6623733540004181B9 /* GlutinExample */, - 96517E6523733540004181B9 /* Products */, - 96517E8423733846004181B9 /* Frameworks */, - ); - sourceTree = ""; - }; - 96517E6523733540004181B9 /* Products */ = { - isa = PBXGroup; - children = ( - 96517E6423733540004181B9 /* GlutinExample.app */, - ); - name = Products; - sourceTree = ""; - }; - 96517E6623733540004181B9 /* GlutinExample */ = { - isa = PBXGroup; - children = ( - 96517E7823733540004181B9 /* Info.plist */, - 96517E7923733540004181B9 /* main.m */, - ); - path = GlutinExample; - sourceTree = ""; - }; - 96517E8023733582004181B9 /* rust */ = { - isa = PBXGroup; - children = ( - 96517E81237337FE004181B9 /* glutin_ios_example.h */, - 96517E8223733807004181B9 /* libglutin_ios_example.a */, - ); - path = rust; - sourceTree = ""; - }; - 96517E8423733846004181B9 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 96517E8D237338BB004181B9 /* CoreFoundation.framework */, - 96517E8B237338A2004181B9 /* Security.framework */, - 96517E8923733863004181B9 /* UIKit.framework */, - 96517E8723733856004181B9 /* OpenGLES.framework */, - 96517E8523733846004181B9 /* GLKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 96517E6323733540004181B9 /* GlutinExample */ = { - isa = PBXNativeTarget; - buildConfigurationList = 96517E7D23733540004181B9 /* Build configuration list for PBXNativeTarget "GlutinExample" */; - buildPhases = ( - 96517E6023733540004181B9 /* Sources */, - 96517E6123733540004181B9 /* Frameworks */, - 96517E6223733540004181B9 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = GlutinExample; - productName = GlutinExample; - productReference = 96517E6423733540004181B9 /* GlutinExample.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 96517E5C23733540004181B9 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1110; - ORGANIZATIONNAME = "Sebastian Imlay"; - TargetAttributes = { - 96517E6323733540004181B9 = { - CreatedOnToolsVersion = 11.1; - }; - }; - }; - buildConfigurationList = 96517E5F23733540004181B9 /* Build configuration list for PBXProject "GlutinExample" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 96517E5B23733540004181B9; - productRefGroup = 96517E6523733540004181B9 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 96517E6323733540004181B9 /* GlutinExample */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 96517E6223733540004181B9 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 96517E6023733540004181B9 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 96517E7A23733540004181B9 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 96517E7B23733540004181B9 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.1; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 96517E7C23733540004181B9 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.1; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 96517E7E23733540004181B9 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = GlutinExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/rust", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.glutin.GlutinExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 96517E7F23733540004181B9 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = GlutinExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/rust", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.glutin.GlutinExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 96517E5F23733540004181B9 /* Build configuration list for PBXProject "GlutinExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 96517E7B23733540004181B9 /* Debug */, - 96517E7C23733540004181B9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 96517E7D23733540004181B9 /* Build configuration list for PBXNativeTarget "GlutinExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 96517E7E23733540004181B9 /* Debug */, - 96517E7F23733540004181B9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 96517E5C23733540004181B9 /* Project object */; -} diff --git a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7ebfdf288e..0000000000 --- a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/glutin_examples/ios-example/GlutinExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/glutin_examples/ios-example/GlutinExample/Info.plist b/glutin_examples/ios-example/GlutinExample/Info.plist deleted file mode 100644 index f4139fabff..0000000000 --- a/glutin_examples/ios-example/GlutinExample/Info.plist +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/glutin_examples/ios-example/GlutinExample/main.m b/glutin_examples/ios-example/GlutinExample/main.m deleted file mode 100644 index cbc1059f12..0000000000 --- a/glutin_examples/ios-example/GlutinExample/main.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// main.m -// GlutinExample -// -// Created by Sebastian Imlay on 11/6/19. -// Copyright © 2019 Sebastian Imlay. All rights reserved. -// - - -#import "glutin_ios_example.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - run_app(); - } -} diff --git a/glutin_examples/ios-example/Makefile b/glutin_examples/ios-example/Makefile deleted file mode 100644 index 3c51f1807e..0000000000 --- a/glutin_examples/ios-example/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -.PHONY: xcode cargo run install simulator-logs - -LIB = rust/libglutin_ios_example.a -run: install - xcrun simctl launch booted com.glutin.GlutinExample - -install: xcode - xcrun simctl install booted build/Build/Products/Debug-iphonesimulator/GlutinExample.app - -xcode: $(LIB) - xcrun xcodebuild -scheme GlutinExample -project GlutinExample.xcodeproj/ -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=13.2' -derivedDataPath build - -cargo: - make -C rust - -simulator-logs: - xcrun simctl spawn booted log stream --level=debug --predicate 'processImagePath endswith "GlutinExample"' -ci: - make -C rust ci - xcrun xcodebuild -scheme GlutinExample -project GlutinExample.xcodeproj/ -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=13.2' -derivedDataPath build - - diff --git a/glutin_examples/ios-example/README.md b/glutin_examples/ios-example/README.md deleted file mode 100644 index 701c9f972b..0000000000 --- a/glutin_examples/ios-example/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# GlutinExample.app - -## Building with `make` -* `make cargo xcode` will build the xcode project and put it in `build/Build/Products/Debug-iphonesimulator/GlutinExample.app` -* `make run` install and run the app in a simulator that's booted. -* `make simulator-logs` will give you way too many logs from the simulator. - -## Building with xcode buttons -* `cd rust && make` -* `open GlutinExample.xcodeproj` and push the play button. diff --git a/glutin_examples/ios-example/rust/.gitignore b/glutin_examples/ios-example/rust/.gitignore deleted file mode 100644 index 320ff59642..0000000000 --- a/glutin_examples/ios-example/rust/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libglutin_ios_example.a diff --git a/glutin_examples/ios-example/rust/Cargo.toml b/glutin_examples/ios-example/rust/Cargo.toml deleted file mode 100644 index 7b6c26f502..0000000000 --- a/glutin_examples/ios-example/rust/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "glutin-ios-example" -version = "0.1.0" -authors = ["Sebastian Imlay "] -edition = '2018' - -[lib] -name = "glutin_ios_example" -crate-type = ["staticlib"] - -[dependencies] -raw-window-handle = "0.3.1" -takeable-option = "0.4" -glutin = { path = "../../../glutin" } - - -[build-dependencies] -gl_generator = "0.14.0" diff --git a/glutin_examples/ios-example/rust/Makefile b/glutin_examples/ios-example/rust/Makefile deleted file mode 100644 index 0e888f2ce7..0000000000 --- a/glutin_examples/ios-example/rust/Makefile +++ /dev/null @@ -1,14 +0,0 @@ - -CARGO_MODE ?= debug -LIB = libglutin_ios_example.a - -all: - cargo lipo - cp ../../../target/universal/debug/$(LIB) ./ - -doc: - cargo doc --target aarch64-apple-ios --open - -ci: - cargo build --target x86_64-apple-ios - cp ../../../target/x86_64-apple-ios/debug/$(LIB) ./ diff --git a/glutin_examples/ios-example/rust/build.rs b/glutin_examples/ios-example/rust/build.rs deleted file mode 100644 index 06d12ecef9..0000000000 --- a/glutin_examples/ios-example/rust/build.rs +++ /dev/null @@ -1,16 +0,0 @@ -extern crate gl_generator; -use gl_generator::{Api, Fallbacks, Profile, Registry}; -use std::env; -use std::fs::File; -use std::path::PathBuf; - -fn main() { - let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); - - println!("cargo:rerun-if-changed=build.rs"); - - let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); - Registry::new(Api::Gles2, (3, 3), Profile::Core, Fallbacks::All, []) - .write_bindings(gl_generator::StructGenerator, &mut file) - .unwrap(); -} diff --git a/glutin_examples/ios-example/rust/glutin_ios_example.h b/glutin_examples/ios-example/rust/glutin_ios_example.h deleted file mode 100644 index fd95ff104f..0000000000 --- a/glutin_examples/ios-example/rust/glutin_ios_example.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void run_app(void); diff --git a/glutin_examples/ios-example/rust/rust_logo.png b/glutin_examples/ios-example/rust/rust_logo.png deleted file mode 100644 index 0ad426d8e6d7f10fcc16acc96ca7f8792ed7852e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13976 zcmXYYcQ_pH_x7@S@7=Px=)LzILDVdQWuqik2|;v%=tPMGL82x~?COcOdO`#dEP{>b ztX@|C?dSWxf6O&=J#*$fGuO;H^PKzKck+F6BWel`3IG5=ZDOo%1ppBE{I`*j;75KC zsPW-<bCB58-0m#*bo#*TnCFZrzS z5|8KFi1ZiJkO#I8LY9^;)w_-2zeN75+tqAs-}O=hW)M`TC%HD)#|l3th0GO;;k zJC+#L>$?Dib54qa@S3o#4X2kX%f)4?}^j=K}LMiS(*YEx) zR)n?o<$s>Kv&HY<2)w#3W1QQoNd=xavPx^kUJHNsD=;Jj1K7+O08?ds8uKp6YZm9} zD{9pK!te|JPuF$o^Q^i_FS$&<^3D=ROqE@4TjQ^ zxOeupp^ju=hCmYEo&X`lQ^WZxwYl9I$8ULgZG`*TWmrc!G_!7!4HaOqR|C=otS$O= z8dmOjb+5Y9`a*{A`CQzBBwWXqY(h6}&}!*(U$8(iAdT8HL&_Rgt<^v4>ivEIPh#fI30Ch>G1&=Ze9o=n9AI`X!-M+ASl#SMOv zE}3}ArS1~vRm0A*V}irC>EF9hUvdjj#@nAs-&;`dGnl_hn;;LM){U1adsieu`*t*ZE@bCB zqNkFMk9a;?LiygHtaJmy*|5+`Hd5vM6Al1FJVuBscIQu2CMBgz=jPP118Q8ctzD)U z1ND(|b4=gS!hN2>K#pgi*JA1^%aG$CocO`Ik8~g|yHrQi3`l$+ zJmVaru5$KCP+R>XQLr)8kJaL|d{9-W=L>|kaP835?+IbJE*UK@Vhc+cW$-U0{G@|2D+T5iq4&%+=&f)V;$%vwrQE>EH#DH= zg_<`o8pYu+-w>2V>Ztq3L*du!z7ATk!<>`H|8kjH98CONzpR$@4o|nh)X3fdHDeX{ zqRIeKEVeT_MRiC!4=2$b>;}|=OgKY$-t16(pZeF#{VvI}&gv(Uc7EZTL@*^b9QkPh zXnwDBGlA&eOkosi7X@U;UJF)efIFUw`YMgsV1@+hZ}(r6b>7)@=V&FZ88Sq96YqB$ zZ}H6n`>jx}Z7zwK0ti8Ux(H^gf&_&;r@D-yDAbI}8HIy;r`e(8ZZjkc;n+9wF$dY2 zU?ry}W1uBV#&G$H1n+1S0nmWoi=irwTk zM99YTj4gdg_I<p?bPguK9c?<@> z{~_YlE+f?flz3^X`BcM~#{b^LTYDt8h#bY7?~f#`cIp8<2Byi5d0Q$I2vfW7!PK=Xk zK5~cxfihb;T`M2z!;TUE{+n!AX|#lg!1THDPXH!boy(})XflNc|0ExZoMxZX%x4!T zJVmL1B5)Ed=^A2zNUl9e+O8%Jhooo!`P7h+UpK>W?2vz{YWy*bB7ktGi0>r4X7Cx= zpH;*tV@%n7;v~wkDYiYFB=E%MqIQzYu2LC?B&MLveg{r&8ZIJuY*6y)0NXO_l4wCC zbQDZe8EXAckK4zIkjlrcOvx1ZMs4n$&e1P~7(qC2z3e4rbCX`qfvKJrG_qFZfF7+n zU&h0}2O)~M**jXHnzf9hD8S%f*6_$x;HAhK(`-WF<0PUZmV{J)}Eu(_&oa(O+ zGoa-s_K*_C$T4>+4gJGQIMKd6W*yaucF?s_OB8AX^G(v?qwawLdh3ob=xTy}vPVkg zm2N+V2WcEX46@5ZHRv3I-dy^d0;$b}5j-uNa;FM5BoZY)Zw(fkp4@R}(166Z*_xm&@#mveK&{zVl9A-LoF)A7|tLj+CyL zN>h6BP~839gDI7i4@qoSMp*j0p(&+Rv12)P=0=um1XqdvuxEmb*!#i30Fcm)!ULwr zQ6FW4Vyg0>0H^IuYVm929Syn%DCdyK%*g>8&otCx`DyPjMVLz*a;o+;U4$)!;kGpS z13T&f1w8|cbN!2Xv#(^gZk6%nTKt)t*4*b4yWwil+)HeOph_~1)qL@5+KK}A@GuAD z+&E@(m#W3^(&|R7MOVHu z^2AB2Pw4Ow+f?+|x5=-sYJ2${9Hs@Y4ReSg9+ow~GG2@OB#7pisi)_E%Qs4nK8XKS z2Qg@}^Pp0sF&W%es2%(oL+kpQ80OOVK|W_X;?*Z2PsUrq!@3ZpO{Lx2OtWDch*3d) zgn#WC@RpDUNMdmGxowB4!~Dv7uGRNM#hA~f+D?7T53*JG(ML=b%(3v_UAR6lRTPWg^j}np(pQe!StclgXW5p$QfI(81PZ9A#N{php144ve)Opll-w7bzX&*VVu zB6dTHIzM{v)^ue$iO3-a8z+%de?ySkN_AtmUGvvVRj@!u?=>8EgDmCc?i<5Xy(Ggc z0P^=lExJYyZ$#YyPPUBEQhU`wVl@|i3W(=F5oq*^aOf9Rlol(1pO=`VD! z_lZpv-G_7M^C&)Tp!XSCCWNxWV{MZf8tIhdr{Q;y55@ar;!X?kr!a7F(??N{wBq+i zCz_lClOMRL(Zvyb6~A;`uz$YoF4Wlo4pWv83ka_pq#@GdD%=^)xfP_(5I zNDKAWFe03H6XY4bC%kX5oLQGzdH?1JQ`DIh?I!RksUVT~a55Gv>bW#dkDBb~8L3t6 zxqX#e5`{|b*HX#0c{U383^*lyX$cy1wWvi7N!2Zq8WOW*-re_EF5xw~lj8xSme({> zkrChdA~;0|ZIx@=-BI`c^RmYfSigaCj_*B&{-{-XhyZe{?W-Eu;MTGr}{5 z=(GY8+E?T%nK)t7?CR1GL>RBZ?3;-`wvZ&DXN^JY&N^&1lVhf3hnvwn>}U`< zRGAk{jq8-NZc@R5Dxprp5AYF0$LuCBo%F8{rG-!^M<2tfOW5YKG}VDzDa3oa^o@aL za(Nlp7&>NmEja1nOCaPzsLTqN$Lr)&0 z^d6#fT8y5P~r2^&u*(XR@LI%`J# zNQzRYs->(2O(ZT-^>XB2X|(WnCg&42iRTG6s3JVhZZr07&UBL^W~oLB7ik>hQwL%f zx~MWY^!zgVL3|}d#Q-5-X2|Uxr3}|FzgK1t;o+P(#SicMs2Lit!@Jipj>xKANbDsw zW*g@la*yC_%jlvw%i}3ae;ejJ@w9M1WXc%|IutkF7vHWZ>QgLYB7H zp1Yq0F#)yiOS@S+!tSEtz$!nMpIqW^&uBI>0T!!UPQVtxjlq=x&?Nh6Q?NGoiMSzb zRscEFQ*{H3_!$I!$jYacDB)a-*-D}-y)T~rI=qvpMz}~x?khIx&I|1Q2r_Rf^*Ce4 zmBcdH>TH{!1=EgaC>qQMBF#LrBcwvty*BvgSxRrTX#7V>PMFV6Hv~YLjPjXnDe%%c z-=BPLTP?zl%o0S?&%xS{amIvX!4m}nB@Rk`VXV4Lsf{tr{Ii-e*$cYM3{AJ#QA)7> zggu2a>pb|al9~~Mz2LGjYFyCE#TZG^fq&w|rNgw6zGMETcMBa--4=Z-hG)AYm5OIeO?bb$IbbqKAJjh^t&BMJq>&O zqhoYoNOvU)H3G`uj~5|bB7C_D^>j*^0gWb{#cCjYcJmMDo0bOGzPw;Irq^Xk>49Kq zMqO3V-*WnpVFC$%tNNE73z%4GhEcK^D;vTr%8}hwQck|J0Ztj2A>O?&y$drmt8k4^Ak>knKc>Ja)^(%8cUEo~j7tXiFn)CP9U6Z%ZU=q7k zZrn;F>rlreyERRWCGC((*5S-%G)>LjjizlRy=FUdrc`)g)wS0rypy)Z!k>BirdG)Z z;*;BnFsC;qI@C$B8#KaxaDVFEB+Xfq7{EAvawQ=?7A}sUpB@WVQDQBW5V!Pf0F_Xp&;zq1>6 zq{Y5dyyw-JI4XyhY|J>6gIlsuMi3~IK2OKN>}@pipGJf$^Tv(7iDBk~-w4EwdK8$W z!^H9b(J0b2Yf!|KzT)6XbWt2L@~MGN`@1G!A;BAH{R(oZ**I*%_2E3CKUV(KgV0+I zy@R}*$qml&A~ou!=xIGr;#CSe_6EW~$fsX3s-tbkCAD}+%qtvhHy-|0BH*Pi`2pQi zCJpbjCvo&VRAK2pU2)u{>XlXW6^ewy%ae<*J5(yJMHiMviHWgW2z+`x7*cMjR7v}r zF6z@6yCja_scb6IKLaFYTa=Lr&mm@pmY~8DXWI7$Zav=DyYKfk_m{p1Zr%WxgKw0F zen-n?EIfzDN~hP!$<0*{eWTJ8n1W-hFq831j5#SX==v$S_)cj_YRu&p`_*%Zu}kyhutH(d^-!8Y@jJoIA_@KeV|%@2HRx0=sHoP;o5$L(i@DNjVTP zsx#vz@!edo=h&o-08(`c9Rm)*&ZlMA@x`-!NI-Lzh0+sC&6~8&ji(qhk{uQ-Td}*5EP$y>dz8SUYu4TnqG5rT$wGxlWz@6BvUzD%2m^i613KU zt^L7#Mr+V1^-0XHY=2q-M@6(w?Wk&vm*+BN}#-)>Zh;&s@?dv2Vd!kpqX#ay`MpYna!{)`~Z zR6M6IK_Nn!?~c(TrK8Lj#nmN?ek^E_6EgSrC~}sxyC@{$R|$uoVDpR9`2DU&9@c-J z?4C*klt=rX>OL!FS@m>#kt4wt8`5w`If;2`<;C$! zipNg%k2dg5#1da$Yn&f8RQ*eOg0#m^$2XF9_zAQVOc>qsr2En8I&M#Z=Y>)B)r4~G zlG~1x-KadL^1Bswu7Aj2ea%R=!tr3gCjN#ws)4OLL!h(quxxm~`+V53E5&9sFDcAg z6~B`Dv{iX?vbs;GTxcT&Jx{Nh#(|0;bIZiITkyq$Up+f<_&89>z`=?WyRx4~kYLV? zd2}NU&q6#s z4$uM6p8CpH7dQJWC7zGj=6y2MPo;i4e`?(w@)@w#X%BQ@!y6fAhehnnq6bui+TXu-;(qB?e{qzo^)u9qq6Id`>Y^(#AND)pwfD)eK4yV1 z;tN|C19~8mjDQ$>2XSX#PCQcL6u0(C>VmC9^B$_@dg#ViMuAqVki@$|^VFO3(sZpN z8v5m(W~zs%*PBe5ow-{YyW%!xmu)@8TA!gxS4WxWV}!xxa**p^S2ELio|LtOwv9}M z4YI6`Uh3(`g2DsKRQ%!Of7If-y0%4)jE=H;j_c3pVsGEm=T)`+*2!CY?{kyvGMIRc}Lo08`~I@FM2JJTFFrP z1FmOCK&ztaOk_5=H7)r!0I{tioHylQ29cMkR|-Xuf7&qVw^8A(&C z9rv|diin7PK2a6uA)f?3b4v|!FFA`UkalXbLK69F9FX#FCOp^NwIy`AKc1KJ`5d(_ zD(MJ(VA+)(O>6x=;Aatf*=a}TQ&igml+qq*2bXR(}#R`f1HJny)hPuyc1sc>z(Q;?c^JT zh6GRgroL9KguUg(-#VVt?JzycEUSAOM)BB5VkrLZqriDq=iQL>!iTd&g=!fZfKCOW z4W-k>7cESt(I1*AU6?;*d`~T}-F@@=10;EnZ&#m#X+Qi@U&nFS(T@>cZ75QK}Ns9O5P`+yW%`8u}$8 z43j`VxzX`6BxHq7f@?^E%*e7x!vP`I8F_vmL@#oJ{R~|TOMC|_Ki-xs>V$h^o{E!q zNGQ!*-LFhA1|NEGV>IFP9j`_2a%aoKQ%A$DqE9U>#+`wV?jTT)D9?{fd=*6Osk|TJ zpmySi2a087=?V#EX;_G{jG|{95AvPEMStS)E zl`d1PA|?^C+HObr2agkDz7?SEU{Pf6qaS%b+)Tpz7W}8}Amtx27>ojrw8FT>So%-< zv;L&klUWkb^as3f6!m{oG&7H^wvJB4Ce!gzxB#O#=9AxvT&DF}qoTuD_%c6S9ckz$ z!MPHM_o}Ve9;GIrLmkkJO+18}7NOQ|@)~^coZbBwHFo(8pCd;}X^20^(E5J^8qgKTi;tVh9XU-i<`usN$N1s-(L6WL_kze4?JLc^I$ zhV;9Q=A<-zpwT@Gd4U5qdEb}-8wVo1a1CoGTCfT!YDK_ z_-KVJrP)!{yWDo+ty~vuCxnB3`Mr;(*UU&4h}!Mdq0zUCY7KID-`s zd@NP2>xE)}Nh=rUY}ZGUTf!F9a9$VB>>ECWdfuVfpXXEmB_JO^b9a4HTdvuOZ(`guJwDX{4owkgLUl#bB_2Y)LX!TgiR#Mg^5At>7o7ADk@&wb-kJBEME-&;rVV5pCXVsTCh(^Xw&(hA(iqyI zU{n@4x+SP8Jw{R|T;z1*K$MkKy-e7LK3fZR-cn~wFKay5kB^^i=;j2h5ya~~4obLo zgXc~8^A1}*6NjPz02a;vxB&8ZbKmrNjoP5-QN#3D+V`ro?lpjrR|l7m$mhh|x|s~9 zUX_XXo*P(FJNL1?cF3aUpL0%h`>lL9Kmp-e;KhZQpsX~1 z3nM*RxfIJM9)IDXyiW_Qk>|O~*fcXl7(BrWtX{Nq==bE9W~iP@ntchMNovt+VDbk} z7b?-4#GUHzu)01csXDBzzQ*b!aY1lWJ_P9LaG9^JOcg9rhHjgo=|Q%ZIYU!Oe-!y} zHvbpVJO2E=M}Af;_4NQC^2=(6itLNgj!n`ldgbz29!1KzaJOzioVxOB*1nHF?=*@i zJ{Szi6LSE#b@h_f;hPOL_SuZv) zu)$Wq91vw*#2y!>6CREEL2}!<0jHlQm|*y$x9y&l*7x;gVkKi66-1QO(O|{jD(#33 zs(9dd)V5Kv0?Sc*qGTs=qjM#b@nH|g)}cz(^ObDiCc|Ld4g39xhDkK>e58$so%Ycj zAJ1+kR$IWFKypxzpd7#A;jpxkky4{{*}If_LX530pI+&N@;T&$!_?@qG!R1cO9Uip zAa58ZAf-E0%5QobZ?^hy5dH|0GX@RJFn~4Ve-iaf2OJ|gAJEokNc79Hk(l97S zmVdYZ{Sl@S?a<>UKc^nZn#Yf`#K>dJ_CAwaQiJ-7GvdDuQ(iUdk8G8XPr9yJW>tgHyD{STh+0 zUGzA=@q1a2+~mY@gWV>#2Fe_Y>``VP(pRSP*XG{skkRK{x~cJ&22irJ#brMj_ig?> zRjLr0J?*U}#B!hyGCU-kD?_}~2Uq_jtlduWGV4mlI+EYrc)vjMj#Qizok3@ylbTgG zqAfn^ci))*L)@uStOaQcSU+pr?U8q*(EiaY5?vB(P)rUGgb(ll;i<>7!VOR95P=`# z5-H|Pq1Vg9i&IWXP4|GH3~YST&rnnV9@)9$b<4F)AgnKTIEGIL8+bTxVMAl^CD~jF zaTf^Frkk6v4O8Q#=uw1Z+SKD{=06S845li+W z+q1mK7a4I4|77An-l2?v;XezC0V0f%Bq<(M)Qg#PN~4@0d`SL>Yo6Rvq>HvT@1##s z=h77ApEyQ2K_TA%)xYLv%f<&Df8%M|b|Rm8@_)KEZ`UW}lX1L-kFMXFj{mILPN%cQ zJU}h|=UQ(KuJ#6XnbhZ*;HB_2rV~-+3ziL04j`Xn{Sc}O{Xp`@dc~~ zVE`D&o4Dg6%(s&T2TBN!cNBs|8U!I9TF}z9$sIwWu7$7uB$h(1Sl^~$lOh=$fgL=K z^Zvc-1BYy>tl!uHwTpEMNNr6}O<0A4VCmF38&9PWjlUj$vmZUKv6FG}u$0 zN|>qnv$eE|nrJoy2P(O7clPfb4Bd4omz${7w!VN-79uV3Y`8~>?8>>Q`cn`KX%!@c zpmw|IcGS1QDzWI4Hh?Vc{9(8W+~~#Rn*J3^(A`Q`H#pJXUFE$=UE7mt+~blIy}Y$q^S=|l z)2N8$rLpE?qSJj523Z=@s^rM!t;a(9Na&Jt(pDo^%dEg@=O5z|s#C1kxBPxB*U@7x z#Tk)!;~{N~^YT*P=riw+1Rpv2M8~Sn>$E-Jy3&<4Jk-7lSNb$fEl=`#8M0jRQC?N; zhcua`Ne1tYMxmj>4>vKUxERrXEz8G2{z*M_rEWX-p0a|rAA_WY`k^)n^^CUy?h_F^ zFMi~>Bi7!R-Dd?`Mko%l?8BC7?2ktP|18PpM9<8><)ahWU$`TA5*TBI3a%yyMHU5C>``k!BD{uWq8iropAY@N}yh6=3Qd+Yn3{VBDaC>mN(?>RZEj?Yv^BnZ%u0QR8MH=lj zD+K&E#CPaP<{src;w#QVy}%P!)d8tj0=+T-oWyK>i72WWm5F$`>MnaBL$sa1c3FJ< z;ell(K4{&>UFk zzn*F(e)mxl-=Gbv`RZ#Hp?GJwA=h&xEPu6Nf-hwZfYDfX@veJs#!#HY+rOCmlx#|T z*I8Z>UnP!+G4gfU<1KUc)B zImmt*BDuZ5sxcNrvLHFH%nYygDG2FEA@h{`B$}p)P2#*Ih70RU+o4!a z45IYCah!5qOQ~2grc?-^gYZwf&%@^=Bkd=#&LuUe)8dB}lx?DU2T@2W*ojOAFp7R6 zvDBY*KODmWeNu9(NK|u+(Ap;_<@}1QNzLS0@kE62=Rl5G)JeoNTjZuEE!o!HD(*Ps zxj?%gA_Jvh8-ma`OfZG49p=em3s<9D$~PQ5Apl23l_<5A{nNI%qUsGPC40jOl@VJ@|!2Q};yW=55|AfgukcR$uOsz+_fKF&60gB(i?8TBy~I%bLRlZSd1a z?&J+Yjf>xVDn*30U)`v_&2-#cycRi~Kak%cxS#u~S#)uF(H>O*>Y6vk6y>&9977*4 zrPgQNa2!ue9+y)EAB{)wt=*^8+A+PN{M2fGgj=Aoczo%R*!?191%ON* ztI-y%Y+fcOJql$YulRyyY>*;(^*O6oetD*lJ}NZr*$n0@$R}E;0;`_;D8rfQ9c5B9 z_+*2JplgZ8`T}7H;L+zI$@$E3tyhd~b-ko;TYAuWA~m*z^l$35evozN&vhqY$j0@< z7J`0r_iqiJk(%WGnkhLbRuHB}pIuFgU5$P1rZ$oKhLG!MH8;8*kHP)&t%oIv+4K9l1kdV3iVn zH7U_kU?2DH1cHctTh1Y2gZ917()UGDJT&;L1^F)jD#dB{gH7GLEeWn5nkX5k*?6~| zQySgZai?_fK(j2gDoRGH_^({SjHH(Rn$OJPE^#dj=Iv)H5>1x*#wJiX8&Z#YWQac28q7BF=;{+<-5J%-t-B2@V#H9s!9gnqZ0tv7N)v8v)0 z#PethHa!|f{b&g#&!8>|LBk`lZAF*tk7{}r`hG&HU3S7h=65aca~u5R(z&7(N-ic= zG(G5?Cf+@J)K1w(k7Js0p8v?P?W&>gG0by-7hb@UcuJ^Vi zoI|X&A*J4m7bH&ww9#Dt4UIWYr+Fw(E#~v-GtU4t6hj2JlvoKZ$&~mLP3g_oDYS11 z4aa!xeDC^J^n~NPr|2X4q=WwM`_=E;W8=sgPn>^y(zV=wPA#2@o6(tV_Wt^pRR$f0 zW4-%Yp3{_Zo_<7*_2%)AqH~ea*Gd+vK<+m<@!a@3yJIssA6!TP?LpoY{(_1@fs`!c z28wgYx{VToVA8?Y-BmE+ z42+pLG0zZTX@BM0eA$gMfY59?2#h#_2>#^t8j3v@@@LEoMTkyCl+_rJ=5d6uDW=;@ zi&u^pP}LUr-y0&hgsRL~R5k@Z(&a4WjfkDw44{lLifxSTd|GJwuxs$3VnBg$L`IVl zO*_wPb!sq5Q)_YvLVZVP+7xf*yXToEFVmy-Wf7n9Jw^=bQ;pY3AJx#DtYZ&VL&wBIN+w>nHImH?Dui0+^&uaR)O@}DoQ)5(xP>cL67{ZNI*%&G@MY)v!RYWD z1A?pO-CHf@cKRx4BzcuvV81*MoU`TkU%{PZ{2buzmNY>H7As!9myu%Rs4Ta z>G*LH-R4A23lPsc-B@Pi)42Qke`L*yEAlK5vCsc_qwiYqCY;s(nTUkzCmmDaahiNf zJbCoI67Tj>kLy1oX?-aEm{#q7Y|sd6@UJqTo&S#M8_>6mkZUOTa804s)LP`XKpz4A zUUk)F{-iXQI#7x!g#z~P0@QSm-8uGwwFjs*)F8?oqDNn{L2ax5kK&3cp@j5`+`HK( zRR_YFfv@%|wzjBh#eF0O2|Qih??qr$vFq2o`TpEU`aH2*eeNO$Xt z<#9dv;k09R7i#)jXOiDn{q;d0zMJy_h7hWh61aM|)@DHF>}Kh7Xn1enYgIiNSCYb~ zZ@SZC{#-@3AZ244Z_qA za=U(tqX3qyY~Q+LtjZ18crV2w#56FQY#k>us&XcjV34^dU*%-F0@%~Zvn1k zXmHfi-Hom3p{QAr1q-I6EL1V-EJg}g21G+%;*{EKG<;r0za(sTwYP8qpS!iwz5b`L zytHD~Tz*p^s5{HQs>d$SD_l>o*Z&_;9M2(F$Ac)AqC!K#+U5UI%0;{=OE$vpuv5&f z@~@aF(we%e(>|Ab&pq+(48h&11z4o^GSYEOLZ$KjaQkhIBI(@0H?rMy)JIA9zMHKR zmSEyOJ%z=p0cYna!214+y?g25AnCT)PEPgtoG06%Tiw^Ez>L0I+KrE@r}1>4FF-A& z9W@jyBYJ)zN8=MR!@^%uameK_;uHA}Iaqz4^N$}|4cV(@!j?pgL+jKHZxPy=Ki7Qe z*6!ovvzRqrEJEjyBrYuZWsWk22NswwC3N)zjv1p-Gzi=U6r15%Cu5`lm$22y#!>@g zaDbaygNA))Qm3=0YfhSC#iKc?|D*&pgtU*ELLr8Qx=Fcdz0OsWf8M`wtDlm*XZ~2O zZkNOkG%j7 zCp&@w`2Ka0aJ@N+mP=FZ@ z-7%3Y-QacC`VsIG)+I<^ajG+)J&Wna;287<9U49K!58NY!(;(JsXjfY^k!+;U0n6eg&8JRe*dh zs-f}foc<Va5t zlmeKB9eU!GEO#%v0vZ$O_~>EAZyK+GW2X;>o|HbqbHl<;*N@7@3NOgV{-^Gl$r9Ht zV)%ALPL4gBT8+J$EqOF6VNvGhqnkulYjpHDHt>61QC55`1aOZLQ3VqX|Dar&d@Z^2 z=^+`obU(DV#I+7^B3A2bWcu%>=y2iQRCrqw!}1v (), - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), - WindowEvent::Touch(_touch) => { - const INCREMENTER: f32 = 0.05; - inc += INCREMENTER; - gl.draw_frame([ - inc % 1.0, - (inc + INCREMENTER) % 1.0, - (inc + INCREMENTER) % 1.0, - 0.0, - ]); - windowed_context.swap_buffers().unwrap(); - } - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, - Event::RedrawRequested(_) => { - gl.draw_frame([0.0; 4]); - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} - -#[no_mangle] -pub extern "C" fn run_app() { - main(); -} diff --git a/glutin_gles2_sys/src/lib.rs b/glutin_gles2_sys/src/lib.rs index 118ff58fb1..e7ff4d9fc9 100644 --- a/glutin_gles2_sys/src/lib.rs +++ b/glutin_gles2_sys/src/lib.rs @@ -1,13 +1,8 @@ #![cfg(target_os = "ios")] -#![allow( - clippy::missing_safety_doc, - clippy::too_many_arguments, - clippy::unused_unit, - non_camel_case_types, - non_snake_case, - non_upper_case_globals -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::manual_non_exhaustive)] +#![allow(clippy::unnecessary_cast)] pub mod gles { include!(concat!(env!("OUT_DIR"), "/gles2_bindings.rs")); diff --git a/glutin_glx_sys/build.rs b/glutin_glx_sys/build.rs index c25ef77504..080d2bf202 100644 --- a/glutin_glx_sys/build.rs +++ b/glutin_glx_sys/build.rs @@ -21,26 +21,22 @@ fn main() { .unwrap(); let mut file = File::create(&dest.join("glx_extra_bindings.rs")).unwrap(); - Registry::new( - Api::Glx, - (1, 4), - Profile::Core, - Fallbacks::All, - [ - "GLX_ARB_context_flush_control", - "GLX_ARB_create_context", - "GLX_ARB_create_context_profile", - "GLX_ARB_create_context_robustness", - "GLX_ARB_fbconfig_float", - "GLX_ARB_framebuffer_sRGB", - "GLX_ARB_multisample", - "GLX_EXT_buffer_age", - "GLX_EXT_framebuffer_sRGB", - "GLX_EXT_swap_control", - "GLX_MESA_swap_control", - "GLX_SGI_swap_control", - ], - ) + Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, [ + "GLX_ARB_context_flush_control", + "GLX_ARB_create_context", + "GLX_ARB_create_context_no_error", + "GLX_ARB_create_context_profile", + "GLX_ARB_create_context_robustness", + "GLX_ARB_fbconfig_float", + "GLX_ARB_framebuffer_sRGB", + "GLX_ARB_multisample", + "GLX_EXT_buffer_age", + "GLX_EXT_create_context_es2_profile", + "GLX_EXT_framebuffer_sRGB", + "GLX_EXT_swap_control", + "GLX_MESA_swap_control", + "GLX_SGI_swap_control", + ]) .write_bindings(gl_generator::StructGenerator, &mut file) .unwrap(); } diff --git a/glutin_glx_sys/src/lib.rs b/glutin_glx_sys/src/lib.rs index 1ecc40a14c..5bec303be2 100644 --- a/glutin_glx_sys/src/lib.rs +++ b/glutin_glx_sys/src/lib.rs @@ -5,13 +5,11 @@ target_os = "netbsd", target_os = "openbsd" ))] -#![allow( - clippy::manual_non_exhaustive, - clippy::missing_safety_doc, - clippy::redundant_static_lifetimes, - clippy::unused_unit -)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::manual_non_exhaustive)] +#![allow(clippy::unused_unit)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::unnecessary_cast)] pub use self::glx::types::GLXContext; pub use x11_dl::xlib::*; @@ -19,6 +17,22 @@ pub use x11_dl::xlib::*; /// GLX bindings pub mod glx { include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs")); + + // The GLX protocol error codes extracted from . + pub const PROTO_BAD_CONTEXT: types::GLenum = 0; + pub const PROTO_BAD_CONTEXT_STATE: types::GLenum = 1; + pub const PROTO_BAD_DRAWABLE: types::GLenum = 2; + pub const PROTO_BAD_PIXMAP: types::GLenum = 3; + pub const PROTO_BAD_CONTEXT_TAG: types::GLenum = 4; + pub const PROTO_BAD_CURRENT_WINDOW: types::GLenum = 5; + pub const PROTO_BAD_RENDER_REQUEST: types::GLenum = 6; + pub const PROTO_BAD_LARGE_REQUEST: types::GLenum = 7; + pub const PROTO_UNSUPPORTED_PRIVATE_REQUEST: types::GLenum = 8; + pub const PROTO_BAD_FBCONFIG: types::GLenum = 9; + pub const PROTO_BAD_PBUFFER: types::GLenum = 10; + pub const PROTO_BAD_CURRENT_DRAWABLE: types::GLenum = 11; + pub const PROTO_BAD_WINDOW: types::GLenum = 12; + pub const PROTO_BAD_PROFILE_ARB: types::GLenum = 13; } /// Functions that are not necessarily always available diff --git a/glutin_wgl_sys/build.rs b/glutin_wgl_sys/build.rs index 2abd679bff..089acc1f55 100644 --- a/glutin_wgl_sys/build.rs +++ b/glutin_wgl_sys/build.rs @@ -16,27 +16,22 @@ fn main() { .unwrap(); let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap(); - Registry::new( - Api::Wgl, - (1, 0), - Profile::Core, - Fallbacks::All, - [ - "WGL_ARB_create_context", - "WGL_ARB_create_context_profile", - "WGL_ARB_create_context_robustness", - "WGL_ARB_context_flush_control", - "WGL_ARB_extensions_string", - "WGL_ARB_framebuffer_sRGB", - "WGL_ARB_multisample", - "WGL_ARB_pixel_format", - "WGL_ARB_pixel_format_float", - "WGL_EXT_create_context_es2_profile", - "WGL_EXT_extensions_string", - "WGL_EXT_framebuffer_sRGB", - "WGL_EXT_swap_control", - ], - ) + Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [ + "WGL_ARB_context_flush_control", + "WGL_ARB_create_context", + "WGL_ARB_create_context_no_error", + "WGL_ARB_create_context_profile", + "WGL_ARB_create_context_robustness", + "WGL_ARB_extensions_string", + "WGL_ARB_framebuffer_sRGB", + "WGL_ARB_multisample", + "WGL_ARB_pixel_format", + "WGL_ARB_pixel_format_float", + "WGL_EXT_create_context_es2_profile", + "WGL_EXT_extensions_string", + "WGL_EXT_framebuffer_sRGB", + "WGL_EXT_swap_control", + ]) .write_bindings(gl_generator::StructGenerator, &mut file) .unwrap(); } diff --git a/glutin_wgl_sys/src/lib.rs b/glutin_wgl_sys/src/lib.rs index 751b5cf5ba..777ee82d20 100644 --- a/glutin_wgl_sys/src/lib.rs +++ b/glutin_wgl_sys/src/lib.rs @@ -1,6 +1,8 @@ #![cfg(any(target_os = "windows"))] -#![allow(clippy::manual_non_exhaustive, clippy::missing_safety_doc, clippy::too_many_arguments)] -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::manual_non_exhaustive)] +#![allow(clippy::unnecessary_cast)] /// WGL bindings pub mod wgl { diff --git a/rustfmt.toml b/rustfmt.toml index 9e91dd035e..8057fee246 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,16 @@ -use_small_heuristics = "Max" +format_code_in_doc_comments = true +match_block_trailing_comma = true +condense_wildcard_suffixes = true use_field_init_shorthand = true +normalize_doc_attributes = true +overflow_delimited_expr = true +imports_granularity = "Module" +use_small_heuristics = "Max" +normalize_comments = true +reorder_impl_items = true +use_try_shorthand = true newline_style = "Unix" -edition = "2018" +format_strings = true +wrap_comments = true +comment_width = 80 +edition = "2021" From 303ff50baa4cfbb9f896a892e92f8d840f859766 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 02:09:44 +0300 Subject: [PATCH 2/8] Use unsafe_op_in_unsafe_fn This also fixes the comments in docs. --- glutin/src/api/cgl/config.rs | 11 ++- glutin/src/api/cgl/context.rs | 32 ++++---- glutin/src/api/cgl/display.rs | 10 +-- glutin/src/api/cgl/surface.rs | 2 +- glutin/src/api/egl/config.rs | 2 +- glutin/src/api/egl/context.rs | 2 +- glutin/src/api/egl/display.rs | 22 +++--- glutin/src/api/egl/mod.rs | 32 ++++---- glutin/src/api/egl/surface.rs | 123 +++++++++++++++-------------- glutin/src/api/glx/config.rs | 2 +- glutin/src/api/glx/context.rs | 2 +- glutin/src/api/glx/display.rs | 37 +++++---- glutin/src/api/glx/mod.rs | 4 +- glutin/src/api/glx/surface.rs | 33 ++++---- glutin/src/api/wgl/config.rs | 15 ++-- glutin/src/api/wgl/context.rs | 2 +- glutin/src/api/wgl/display.rs | 19 +++-- glutin/src/api/wgl/mod.rs | 104 ++++++++++++++---------- glutin/src/api/wgl/surface.rs | 4 +- glutin/src/context.rs | 16 ++-- glutin/src/display.rs | 76 ++++++++++-------- glutin/src/error.rs | 8 +- glutin/src/lib.rs | 18 +++-- glutin/src/lib_loading.rs | 3 +- glutin/src/platform/x11.rs | 14 ++-- glutin/src/prelude.rs | 5 +- glutin/src/surface.rs | 8 +- glutin_examples/examples/window.rs | 2 +- 28 files changed, 334 insertions(+), 274 deletions(-) diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs index 4d5a8db5b0..aa28a70700 100644 --- a/glutin/src/api/cgl/config.rs +++ b/glutin/src/api/cgl/config.rs @@ -78,10 +78,13 @@ impl Display { // Terminate attrs with zero. attrs.push(0); - let raw = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs); - if raw.is_null() { - return Err(ErrorKind::BadConfig.into()); - } + let raw = unsafe { + let raw = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs); + if raw.is_null() { + return Err(ErrorKind::BadConfig.into()); + } + raw + }; let inner = Arc::new(ConfigInner { display: self.clone(), diff --git a/glutin/src/api/cgl/context.rs b/glutin/src/api/cgl/context.rs index 8b2ee65e88..4c50aa5228 100644 --- a/glutin/src/api/cgl/context.rs +++ b/glutin/src/api/cgl/context.rs @@ -41,23 +41,25 @@ impl Display { return Err(ErrorKind::NotSupported("robustness is not supported with CGL").into()); } - let config = config.clone(); - let raw = NSOpenGLContext::alloc(nil) - .initWithFormat_shareContext_(*config.inner.raw, share_context as *mut _); - - if config.inner.transrarency { - let opacity = 0; - super::check_error(CGLSetParameter( - raw.CGLContextObj().cast(), - cgl::kCGLCPSurfaceOpacity, - &opacity, - ))?; - } + unsafe { + let config = config.clone(); + let raw = NSOpenGLContext::alloc(nil) + .initWithFormat_shareContext_(*config.inner.raw, share_context as *mut _); + + if config.inner.transrarency { + let opacity = 0; + super::check_error(CGLSetParameter( + raw.CGLContextObj().cast(), + cgl::kCGLCPSurfaceOpacity, + &opacity, + ))?; + } - let inner = ContextInner { display: self.clone(), config, raw: NSOpenGLContextId(raw) }; - let context = NotCurrentContext { inner }; + let inner = ContextInner { display: self.clone(), config, raw: NSOpenGLContextId(raw) }; + let context = NotCurrentContext { inner }; - Ok(context) + Ok(context) + } } } diff --git a/glutin/src/api/cgl/display.rs b/glutin/src/api/cgl/display.rs index 0674cb4744..876ca090af 100644 --- a/glutin/src/api/cgl/display.rs +++ b/glutin/src/api/cgl/display.rs @@ -43,7 +43,7 @@ impl GlDisplay for Display { &self, template: ConfigTemplate, ) -> Result + '_>> { - Self::find_configs(self, template) + unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( @@ -51,7 +51,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_window_surface(self, config, surface_attributes) + unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( @@ -59,7 +59,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pbuffer_surface(self, config, surface_attributes) + unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( @@ -67,7 +67,7 @@ impl GlDisplay for Display { config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { - Self::create_context(self, config, context_attributes) + unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( @@ -75,7 +75,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pixmap_surface(self, config, surface_attributes) + unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } } diff --git a/glutin/src/api/cgl/surface.rs b/glutin/src/api/cgl/surface.rs index 8ee43f7d9d..8fd012718d 100644 --- a/glutin/src/api/cgl/surface.rs +++ b/glutin/src/api/cgl/surface.rs @@ -29,7 +29,7 @@ impl Display { Err(ErrorKind::NotSupported("pixmaps are not supported with CGL").into()) } - pub(crate) fn create_pbuffer_surface( + pub(crate) unsafe fn create_pbuffer_surface( &self, _config: &Config, _surface_attributes: &SurfaceAttributes, diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs index ed7200eda6..11fe569ab3 100644 --- a/glutin/src/api/egl/config.rs +++ b/glutin/src/api/egl/config.rs @@ -24,7 +24,7 @@ const FLOAT_PIXELS_EXT: &str = "EGL_EXT_pixel_format_float"; const SRGB_SURFACE: &str = "EGL_KHR_gl_colorspace"; impl Display { - pub(crate) fn find_configs( + pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { diff --git a/glutin/src/api/egl/context.rs b/glutin/src/api/egl/context.rs index 67c455cf96..954a52958c 100644 --- a/glutin/src/api/egl/context.rs +++ b/glutin/src/api/egl/context.rs @@ -23,7 +23,7 @@ use super::display::Display; use super::surface::Surface; impl Display { - pub(crate) fn create_context( + pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index 825429188a..e8b3d5f16d 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -72,12 +72,14 @@ impl Display { } })?; - let (mut major, mut minor) = (0, 0); - if egl.Initialize(display, &mut major, &mut minor) == egl::FALSE { - return Err(super::check_error().err().unwrap()); - } + 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().err().unwrap()); + } - let version = Version::new(major as u8, minor as u8); + Version::new(major as u8, minor as u8) + }; // Load extensions. let client_extensions = get_extensions(egl, display); @@ -232,7 +234,7 @@ impl GlDisplay for Display { &self, template: ConfigTemplate, ) -> Result + '_>> { - Self::find_configs(self, template) + unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( @@ -240,7 +242,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_window_surface(self, config, surface_attributes) + unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( @@ -248,7 +250,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pbuffer_surface(self, config, surface_attributes) + unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( @@ -256,7 +258,7 @@ impl GlDisplay for Display { config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { - Self::create_context(self, config, context_attributes) + unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( @@ -264,7 +266,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pixmap_surface(self, config, surface_attributes) + unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } } diff --git a/glutin/src/api/egl/mod.rs b/glutin/src/api/egl/mod.rs index 828c15d89c..e7605c6ca7 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -28,7 +28,7 @@ pub(crate) static EGL: Lazy> = Lazy::new(|| { #[cfg(not(windows))] let paths = ["libEGL.so.1", "libEGL.so"]; - SymWrapper::new(&paths).map(Egl).ok() + unsafe { SymWrapper::new(&paths).map(Egl).ok() } }); type EglGetProcAddress = unsafe extern "C" fn(*const ffi::c_void) -> *const ffi::c_void; @@ -42,21 +42,23 @@ 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 { - let sym_name = CString::new(sym_name.as_bytes()).unwrap(); - if let Ok(sym) = lib.get(sym_name.as_bytes_with_nul()) { - return *sym; + 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) } - - 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) }; Self::load_with(loader) diff --git a/glutin/src/api/egl/surface.rs b/glutin/src/api/egl/surface.rs index 4a05ae1655..46397f5096 100644 --- a/glutin/src/api/egl/surface.rs +++ b/glutin/src/api/egl/surface.rs @@ -58,11 +58,13 @@ impl Display { attrs.push(egl::NONE as EGLint); let config = config.clone(); - let surface = Self::check_surface_error(self.inner.egl.CreatePbufferSurface( - *self.inner.raw, - *config.inner.raw, - attrs.as_ptr(), - ))?; + 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(), @@ -97,31 +99,33 @@ impl Display { attrs.push(egl::NONE as EGLAttrib); let config = config.clone(); - let surface = if self.inner.version >= Version::new(1, 5) - && self.inner.egl.CreatePlatformWindowSurface.is_loaded() - { - self.inner.egl.CreatePlatformPixmapSurface( - *self.inner.raw, - *config.inner.raw, - native_pixmap.raw(), - attrs.as_ptr(), - ) - } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { - let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); - self.inner.egl.CreatePlatformPixmapSurfaceEXT( - *self.inner.raw, - *config.inner.raw, - native_pixmap.raw(), - attrs.as_ptr(), - ) - } else { - let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); - self.inner.egl.CreatePixmapSurface( - *self.inner.raw, - *config.inner.raw, - native_pixmap.raw(), - attrs.as_ptr(), - ) + let surface = unsafe { + if self.inner.version >= Version::new(1, 5) + && self.inner.egl.CreatePlatformWindowSurface.is_loaded() + { + self.inner.egl.CreatePlatformPixmapSurface( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ) + } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePlatformPixmapSurfaceEXT( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ) + } else { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePixmapSurface( + *self.inner.raw, + *config.inner.raw, + native_pixmap.raw(), + attrs.as_ptr(), + ) + } }; let surface = Self::check_surface_error(surface)?; @@ -174,31 +178,33 @@ impl Display { let config = config.clone(); - let surface = if self.inner.version >= Version::new(1, 5) - && self.inner.egl.CreatePlatformWindowSurface.is_loaded() - { - self.inner.egl.CreatePlatformWindowSurface( - *self.inner.raw, - *config.inner.raw, - native_window.raw() as _, - attrs.as_ptr(), - ) - } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { - let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); - self.inner.egl.CreatePlatformWindowSurfaceEXT( - *self.inner.raw, - *config.inner.raw, - native_window.raw() as _, - attrs.as_ptr(), - ) - } else { - let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); - self.inner.egl.CreateWindowSurface( - *self.inner.raw, - *config.inner.raw, - native_window.raw() as _, - attrs.as_ptr(), - ) + let surface = unsafe { + if self.inner.version >= Version::new(1, 5) + && self.inner.egl.CreatePlatformWindowSurface.is_loaded() + { + self.inner.egl.CreatePlatformWindowSurface( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + attrs.as_ptr(), + ) + } else if self.inner.client_extensions.contains("EGL_EXT_platform_base") { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreatePlatformWindowSurfaceEXT( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + attrs.as_ptr(), + ) + } else { + let attrs: Vec = attrs.into_iter().map(|attr| attr as EGLint).collect(); + self.inner.egl.CreateWindowSurface( + *self.inner.raw, + *config.inner.raw, + native_window.raw() as _, + attrs.as_ptr(), + ) + } }; let surface = Self::check_surface_error(surface)?; @@ -233,11 +239,10 @@ pub struct Surface { impl Surface { /// Swaps the underlying back buffers when the surface is not single /// buffered and pass the [`DamageRect`] information to the system - /// compositor. Providing empty slice will result in damaging the entire - /// surface. + /// compositor. Providing empty slice will damage the entire surface. /// - /// This Api doesn't do any partial rendering, it just sends the provides - /// the hints for the system compositor. + /// 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, diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index 7c659e9b34..32d73bce7e 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -23,7 +23,7 @@ use super::display::Display; const FLOAT_PIXEL_EXT: &str = "GLX_ARB_fbconfig_float"; impl Display { - pub(crate) fn find_configs( + pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { diff --git a/glutin/src/api/glx/context.rs b/glutin/src/api/glx/context.rs index 4633cc69bf..363a5747e7 100644 --- a/glutin/src/api/glx/context.rs +++ b/glutin/src/api/glx/context.rs @@ -25,7 +25,7 @@ use super::display::Display; use super::surface::Surface; impl Display { - pub(crate) fn create_context( + pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs index cc5bbecdcc..4c1837cd61 100644 --- a/glutin/src/api/glx/display.rs +++ b/glutin/src/api/glx/display.rs @@ -58,13 +58,15 @@ impl Display { }; // Set the base for errors coming from GLX. - let mut error_base = 0; - let mut event_base = 0; - if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { - // The glx extension isn't present. - return Err(ErrorKind::InitializationFailed.into()); + 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); } - 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 @@ -74,12 +76,13 @@ impl Display { // // The easiest way to do this is to just call `glXQueryVersion()` before // doing anything else. See: https://www.virtualbox.org/ticket/8293 - let (mut major, mut minor) = (0, 0); - if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { - return Err(ErrorKind::InitializationFailed.into()); - } - - let version = Version::new(major as u8, minor as u8); + 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()); @@ -114,7 +117,7 @@ impl GlDisplay for Display { &self, template: ConfigTemplate, ) -> Result + '_>> { - Self::find_configs(self, template) + unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( @@ -122,7 +125,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_window_surface(self, config, surface_attributes) + unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( @@ -130,7 +133,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pbuffer_surface(self, config, surface_attributes) + unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( @@ -138,7 +141,7 @@ impl GlDisplay for Display { config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { - Self::create_context(self, config, context_attributes) + unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( @@ -146,7 +149,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pixmap_surface(self, config, surface_attributes) + unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } } diff --git a/glutin/src/api/glx/mod.rs b/glutin/src/api/glx/mod.rs index dfd19c576f..25d3b2649e 100644 --- a/glutin/src/api/glx/mod.rs +++ b/glutin/src/api/glx/mod.rs @@ -41,7 +41,7 @@ static LAST_GLX_ERROR: Lazy>> = Lazy::new(|| Mutex::new(None static GLX: Lazy> = Lazy::new(|| { let paths = ["libGL.so.1", "libGL.so"]; - SymWrapper::new(&paths).map(Glx).ok() + unsafe { SymWrapper::new(&paths).map(Glx).ok() } }); static GLX_EXTRA: Lazy> = Lazy::new(|| { @@ -56,7 +56,7 @@ unsafe impl Send for Glx {} impl SymLoading for glx::Glx { unsafe fn load_with(lib: &Library) -> Self { - Self::load_with(|sym| { + 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()) diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs index 323cc1a478..4be85e8bcc 100644 --- a/glutin/src/api/glx/surface.rs +++ b/glutin/src/api/glx/surface.rs @@ -47,12 +47,14 @@ impl Display { attrs.push(glx::NONE as c_int); let config = config.clone(); - let surface = self.inner.glx.CreatePixmap( - self.inner.raw.cast(), - *config.inner.raw, - xid, - attrs.as_ptr(), - ); + let surface = unsafe { + self.inner.glx.CreatePixmap( + self.inner.raw.cast(), + *config.inner.raw, + xid, + attrs.as_ptr(), + ) + }; super::last_glx_error(self.inner.raw)?; @@ -86,8 +88,9 @@ impl Display { attrs.push(glx::NONE as c_int); let config = config.clone(); - let surface = - self.inner.glx.CreatePbuffer(self.inner.raw.cast(), *config.inner.raw, attrs.as_ptr()); + let surface = unsafe { + self.inner.glx.CreatePbuffer(self.inner.raw.cast(), *config.inner.raw, attrs.as_ptr()) + }; super::last_glx_error(self.inner.raw)?; @@ -120,12 +123,14 @@ impl Display { attrs.push(glx::NONE as c_int); let config = config.clone(); - let surface = self.inner.glx.CreateWindow( - self.inner.raw.cast(), - *config.inner.raw, - window, - attrs.as_ptr() as *const _, - ); + let surface = unsafe { + self.inner.glx.CreateWindow( + self.inner.raw.cast(), + *config.inner.raw, + window, + attrs.as_ptr() as *const _, + ) + }; super::last_glx_error(self.inner.raw)?; diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index d3f2cf63df..c1eec3d6f8 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -31,7 +31,7 @@ const SRGB_ARB: &str = "WGL_ARB_framebuffer_sRGB"; const SRGB_EXT: &str = "WGL_ARB_framebuffer_sRGB"; impl Display { - pub(crate) fn find_configs( + pub(crate) unsafe fn find_configs( &self, template: ConfigTemplate, ) -> Result + '_>> { @@ -274,16 +274,19 @@ impl Config { /// 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) => gdi::GetDC(window.hwnd as _), + RawWindowHandle::Win32(window) => unsafe { gdi::GetDC(window.hwnd as _) }, _ => return Err(ErrorKind::BadNativeWindow.into()), }; let descriptor = self.inner.descriptor.as_ref().map(|desc| desc as _).unwrap_or(std::ptr::null()); - if gl::SetPixelFormat(hdc, self.inner.pixel_format_index, descriptor) == 0 { - Err(IoError::last_os_error().into()) - } else { - Ok(()) + + unsafe { + if gl::SetPixelFormat(hdc, self.inner.pixel_format_index, descriptor) == 0 { + Err(IoError::last_os_error().into()) + } else { + Ok(()) + } } } diff --git a/glutin/src/api/wgl/context.rs b/glutin/src/api/wgl/context.rs index fec734f9f6..b2a099dbcf 100644 --- a/glutin/src/api/wgl/context.rs +++ b/glutin/src/api/wgl/context.rs @@ -29,7 +29,7 @@ use super::display::Display; use super::surface::Surface; impl Display { - pub(crate) fn create_context( + pub(crate) unsafe fn create_context( &self, config: &Config, context_attributes: &ContextAttributes, diff --git a/glutin/src/api/wgl/display.rs b/glutin/src/api/wgl/display.rs index 0ca78e8ffc..1e5e6a4591 100644 --- a/glutin/src/api/wgl/display.rs +++ b/glutin/src/api/wgl/display.rs @@ -50,7 +50,7 @@ impl Display { let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()).collect::>(); - let lib_opengl32 = dll_loader::LoadLibraryW(name.as_ptr()); + let lib_opengl32 = unsafe { dll_loader::LoadLibraryW(name.as_ptr()) }; if lib_opengl32 == 0 { return Err(ErrorKind::NotFound.into()); } @@ -58,8 +58,11 @@ impl Display { // In case native window was provided init extra functions. let (wgl_extra, client_extensions) = if let Some(RawWindowHandle::Win32(window)) = native_window { - let (wgl_extra, client_extensions) = super::load_extra_functions(window.hwnd as _)?; - (Some(wgl_extra), client_extensions) + unsafe { + let (wgl_extra, client_extensions) = + super::load_extra_functions(window.hinstance as _, window.hwnd as _)?; + (Some(wgl_extra), client_extensions) + } } else { (None, HashSet::new()) }; @@ -80,7 +83,7 @@ impl GlDisplay for Display { &self, template: ConfigTemplate, ) -> Result + '_>> { - Self::find_configs(self, template) + unsafe { Self::find_configs(self, template) } } unsafe fn create_window_surface( @@ -88,7 +91,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_window_surface(self, config, surface_attributes) + unsafe { Self::create_window_surface(self, config, surface_attributes) } } unsafe fn create_pbuffer_surface( @@ -96,7 +99,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pbuffer_surface(self, config, surface_attributes) + unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } } unsafe fn create_context( @@ -104,7 +107,7 @@ impl GlDisplay for Display { config: &Self::Config, context_attributes: &crate::context::ContextAttributes, ) -> Result { - Self::create_context(self, config, context_attributes) + unsafe { Self::create_context(self, config, context_attributes) } } unsafe fn create_pixmap_surface( @@ -112,7 +115,7 @@ impl GlDisplay for Display { config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - Self::create_pixmap_surface(self, config, surface_attributes) + unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } } } diff --git a/glutin/src/api/wgl/mod.rs b/glutin/src/api/wgl/mod.rs index 2b011c51c7..87e51a9c31 100644 --- a/glutin/src/api/wgl/mod.rs +++ b/glutin/src/api/wgl/mod.rs @@ -9,9 +9,8 @@ use std::os::windows::ffi::OsStrExt; use glutin_wgl_sys::{wgl, wgl_extra}; use once_cell::sync::OnceCell; -use windows_sys::Win32::Foundation::HWND; +use windows_sys::Win32::Foundation::{HINSTANCE, HWND}; use windows_sys::Win32::Graphics::{Gdi as gdi, OpenGL as gl}; -use windows_sys::Win32::System::LibraryLoader as dll_loader; use windows_sys::Win32::UI::WindowsAndMessaging::{self as wm, WINDOWPLACEMENT, WNDCLASSEXW}; use crate::error::{Error, ErrorKind, Result}; @@ -46,24 +45,34 @@ impl Deref for WglExtra { } } -unsafe fn load_extra_functions(win: HWND) -> Result<(&'static WglExtra, HashSet<&'static str>)> { - 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()); - } - let rect = placement.rcNormalPosition; +unsafe fn load_extra_functions( + instance: HINSTANCE, + 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]; - if wm::GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { - return Err(IoError::last_os_error().into()); + unsafe { + if wm::GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 { + return Err(IoError::last_os_error().into()); + } } - let instance = dll_loader::GetModuleHandleW(std::ptr::null()); - 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()); - } + 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).into_iter()).collect::>(); @@ -76,7 +85,7 @@ unsafe fn load_extra_functions(win: HWND) -> Result<(&'static WglExtra, HashSet< // worked. Multiple registrations of the window class trigger an // error which we want to ignore silently (e.g for multi-window // setups). - wm::RegisterClassExW(&class); + unsafe { wm::RegisterClassExW(&class) }; // This dummy wnidow should match the real one enough to get the same OpenGL // driver. @@ -85,42 +94,51 @@ unsafe fn load_extra_functions(win: HWND) -> Result<(&'static WglExtra, HashSet< let ex_style = wm::WS_EX_APPWINDOW; let style = wm::WS_POPUP | wm::WS_CLIPSIBLINGS | wm::WS_CLIPCHILDREN; - let win = 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(), - ); + 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 = gdi::GetDC(win); - 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 = gl::wglCreateContext(hdc); - if gl::wglMakeCurrent(hdc, context) == 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); - wm::DestroyWindow(win); - gl::wglDeleteContext(context); + unsafe { + wm::DestroyWindow(win); + gl::wglDeleteContext(context); + } Ok((wgl_extra, client_extensions)) } diff --git a/glutin/src/api/wgl/surface.rs b/glutin/src/api/wgl/surface.rs index a8c3dc8670..b640860d51 100644 --- a/glutin/src/api/wgl/surface.rs +++ b/glutin/src/api/wgl/surface.rs @@ -32,7 +32,7 @@ impl Display { Err(ErrorKind::NotSupported("pixmaps are not implemented with WGL").into()) } - pub(crate) fn create_pbuffer_surface( + pub(crate) unsafe fn create_pbuffer_surface( &self, _config: &Config, _surface_attributes: &SurfaceAttributes, @@ -47,7 +47,7 @@ impl Display { ) -> Result> { let hwnd = match surface_attributes.raw_window_handle.as_ref().unwrap() { handle @ RawWindowHandle::Win32(window_handle) => { - let _ = config.apply_on_native_window(handle); + let _ = unsafe { config.apply_on_native_window(handle) }; window_handle.hwnd as HWND }, _ => { diff --git a/glutin/src/context.rs b/glutin/src/context.rs index 46c07c515a..f53ebccb35 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -57,7 +57,7 @@ pub trait NotCurrentGlContextSurfaceAccessor: Sealed { /// /// # Api-specific: /// - /// **WGL/CGL: ** - Not supported. + /// **WGL/CGL:** - not supported. fn make_current_draw_read( self, surface_draw: &Self::Surface, @@ -96,7 +96,7 @@ pub trait PossiblyCurrentContextGlSurfaceAccessor: Sealed { /// /// # Api-specific: /// - /// This is not supported with WGL and CGL. + /// **CGL/WGL:** - not supported. fn make_current_draw_read( &self, surface_draw: &Self::Surface, @@ -139,7 +139,7 @@ impl ContextAttributesBuilder { /// /// # Platform-specific /// - /// On Wayland both context must use the same Wayland connection. + /// On Wayland both contexts must use the same Wayland connection. /// /// [`Config`]: crate::config::Config pub fn with_sharing(mut self, context: &impl AsRawContext) -> Self { @@ -323,7 +323,7 @@ pub enum ReleaseBehaviour { /// /// # Api-specific /// - /// **macOS:** no supported, [`Self::Flush`] is always used. + /// **macOS:** - not supported, [`Self::Flush`] is always used. None, /// Flushes the context that was previously current as if `glFlush` was @@ -454,16 +454,14 @@ impl Sealed for NotCurrentContext {} /// 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`]. -/// ```no_run +/// ```compile_fail /// fn test_send() {} -/// test_send::(); +/// test_send::(); /// ``` /// -/// However it's not `Sync`. -/// /// ```compile_fail /// fn test_sync() {} -/// test_sync::(); +/// test_sync::(); /// ``` /// [`make it not current`]: crate::context::PossiblyCurrentGlContext::make_not_current #[derive(Debug)] diff --git a/glutin/src/display.rs b/glutin/src/display.rs index 765df9c15c..5c95ed4a10 100644 --- a/glutin/src/display.rs +++ b/glutin/src/display.rs @@ -37,11 +37,11 @@ pub trait GlDisplay: Sealed { /// A context that is being used by the display. type NotCurrentContext: NotCurrentGlContext; - /// Find configuration matching the given `template`. + /// Find configurations matching the given `template`. /// /// # Safety /// - /// Some platforms use [`RawWindowHandle`] to perform config picking, so it + /// Some platforms use [`RawWindowHandle`] to pick configs, so it /// must point to a valid object if it was passed on /// [`crate::config::ConfigTemplate`]. /// @@ -82,7 +82,7 @@ pub trait GlDisplay: Sealed { /// /// # Safety /// - /// The function is safe in general, but mark as not for compatibility + /// The function is safe in general, but marked as not for compatibility /// reasons. unsafe fn create_pbuffer_surface( &self, @@ -122,7 +122,7 @@ pub trait AsRawDisplay { /// The graphics display to handle underlying graphics platform in a /// cross-platform way. /// -/// The display could be accessed from any thread. +/// The display can be accessed from any thread. /// /// ```no_run /// fn test_send() {} @@ -152,9 +152,17 @@ pub enum Display { 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. + /// The `display` must point to the valid platform display and be valid for + /// the entire lifetime of all Objects created with that display. + /// + /// The `picker` must contain pointers to the valid values if GLX or WGL + /// specific options were used. pub unsafe fn from_raw(display: RawDisplayHandle, picker: DisplayPicker) -> Result { #[cfg(glx_backend)] let registrar = picker @@ -163,18 +171,20 @@ impl Display { match picker.api_preference { #[cfg(egl_backend)] - DisplayApiPreference::Egl => Ok(Self::Egl(EglDisplay::from_raw(display)?)), + DisplayApiPreference::Egl => unsafe { Ok(Self::Egl(EglDisplay::from_raw(display)?)) }, #[cfg(glx_backend)] - DisplayApiPreference::Glx => Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)), + DisplayApiPreference::Glx => unsafe { + Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)) + }, #[cfg(wgl_backend)] - DisplayApiPreference::Wgl => { + DisplayApiPreference::Wgl => unsafe { Ok(Self::Wgl(WglDisplay::from_raw(display, picker.window_handle)?)) }, #[cfg(cgl_backend)] DisplayApiPreference::Cgl => Ok(Self::Cgl(CglDisplay::from_raw(display)?)), #[cfg(all(egl_backend, glx_backend))] - DisplayApiPreference::EglThenGlx => { + DisplayApiPreference::EglThenGlx => unsafe { if let Ok(display) = EglDisplay::from_raw(display) { Ok(Self::Egl(display)) } else { @@ -182,7 +192,7 @@ impl Display { } }, #[cfg(all(egl_backend, glx_backend))] - DisplayApiPreference::GlxThenEgl => { + DisplayApiPreference::GlxThenEgl => unsafe { if let Ok(display) = GlxDisplay::from_raw(display, registrar) { Ok(Self::Glx(display)) } else { @@ -191,7 +201,7 @@ impl Display { }, #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::EglThenWgl => { + DisplayApiPreference::EglThenWgl => unsafe { if let Ok(display) = EglDisplay::from_raw(display) { Ok(Self::Egl(display)) } else { @@ -199,7 +209,7 @@ impl Display { } }, #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::WglThenEgl => { + DisplayApiPreference::WglThenEgl => unsafe { if let Ok(display) = WglDisplay::from_raw(display, picker.window_handle) { Ok(Self::Wgl(display)) } else { @@ -223,19 +233,19 @@ impl GlDisplay for Display { ) -> Result + '_>> { match self { #[cfg(egl_backend)] - Self::Egl(display) => { + Self::Egl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Egl))) }, #[cfg(glx_backend)] - Self::Glx(display) => { + Self::Glx(display) => unsafe { Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Glx))) }, #[cfg(wgl_backend)] - Self::Wgl(display) => { + Self::Wgl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Wgl))) }, #[cfg(cgl_backend)] - Self::Cgl(display) => { + Self::Cgl(display) => unsafe { Ok(Box::new(display.find_configs(template)?.into_iter().map(Config::Cgl))) }, } @@ -248,19 +258,19 @@ impl GlDisplay for Display { ) -> Result { match (self, config) { #[cfg(egl_backend)] - (Self::Egl(display), Config::Egl(config)) => { + (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)) => { + (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)) => { + (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)) => { + (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(NotCurrentContext::Cgl(display.create_context(config, context_attributes)?)) }, _ => unreachable!(), @@ -274,19 +284,19 @@ impl GlDisplay for Display { ) -> Result { match (self, config) { #[cfg(egl_backend)] - (Self::Egl(display), Config::Egl(config)) => { + (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)) => { + (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)) => { + (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)) => { + (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_window_surface(config, surface_attributes)?)) }, _ => unreachable!(), @@ -300,19 +310,19 @@ impl GlDisplay for Display { ) -> Result { match (self, config) { #[cfg(egl_backend)] - (Self::Egl(display), Config::Egl(config)) => { + (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)) => { + (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)) => { + (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)) => { + (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_pbuffer_surface(config, surface_attributes)?)) }, _ => unreachable!(), @@ -326,19 +336,19 @@ impl GlDisplay for Display { ) -> Result { match (self, config) { #[cfg(egl_backend)] - (Self::Egl(display), Config::Egl(config)) => { + (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)) => { + (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)) => { + (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)) => { + (Self::Cgl(display), Config::Cgl(config)) => unsafe { Ok(Surface::Cgl(display.create_pixmap_surface(config, surface_attributes)?)) }, _ => unreachable!(), @@ -354,7 +364,7 @@ impl AsRawDisplay for Display { impl Sealed for Display {} -/// A settings to control automatic display picking. +/// Settings to control automatic display picking. pub struct DisplayPicker { pub(crate) api_preference: DisplayApiPreference, #[cfg(glx_backend)] diff --git a/glutin/src/error.rs b/glutin/src/error.rs index fc13dc57ac..7ea25f5698 100644 --- a/glutin/src/error.rs +++ b/glutin/src/error.rs @@ -40,7 +40,7 @@ impl Error { self.kind } - /// The underlying raw code if it's present. + /// The underlying raw code in case it's present. #[inline] pub fn raw_code(&self) -> Option { self.raw_code @@ -92,7 +92,7 @@ pub enum ErrorKind { /// enough memory. OutOfMemory, - /// Anrecognized attribute value was passed. + /// An recognized attribute value was passed. BadAttribute, /// The context is no longer valid. @@ -119,7 +119,7 @@ pub enum ErrorKind { /// The pixmap is invalid. BadPixmap, - /// Arguments are inconsistent. For example when sharad context are not + /// Arguments are inconsistent. For example when shared contexts are not /// compatible. BadMatch, @@ -138,7 +138,7 @@ pub enum ErrorKind { /// The operation is not supported by the platform. NotSupported(&'static str), - /// The misc error that can't be classyfied occured. + /// The misc error that can't be classified occurred. Misc, } diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs index de7ff74400..8662447141 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -1,15 +1,15 @@ //! The purpose of this library is to provide OpenGL `[context]` on as many -//! platforms as possible abstracting away the underlying platforms shenanigans, -//! while keeping direct access to them to make the use of platform specific -//! extensions easier. +//! platforms as possible abstracting away the platform inconsistencies, +//! while providing direct access to them to make the use of platform specific +//! extensions when needed. //! -//! However the library doesn't force into using a cross platform abstraction, +//! Glutin does not force to use the cross platform abstraction, //! for example, when only `[EGL]` is desired, it can be used directly. //! //! The initialization starts by loading and connecting to the underlying -//! graphics platform Api when creating a `[display]`. This object is used to -//! create all the OpenGL objects, such as `[config]`, `[context]`, and -//! `[surface]`. +//! graphics platform Api when creating a `[display]`. A display is used to +//! create all the common platform Api objects, such as `[config]`, `[context]`, +//! and `[surface]`. //! //! [display]: crate::display //! [context]: crate::context @@ -19,6 +19,8 @@ #![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)] @@ -46,7 +48,7 @@ extern crate objc; pub(crate) mod private { /// Prevent traits from being implemented downstream, since those are used /// purely for documentation organization and simplify platform api - /// implementation maintaining. + /// implementation maintenance. pub trait Sealed {} /// `gl_api_dispatch!(match expr; Enum(foo) => foo.something())` diff --git a/glutin/src/lib_loading.rs b/glutin/src/lib_loading.rs index afb84b457a..679b5cd44d 100644 --- a/glutin/src/lib_loading.rs +++ b/glutin/src/lib_loading.rs @@ -9,6 +9,7 @@ use libloading::Library; 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; } @@ -20,7 +21,7 @@ pub struct SymWrapper { } impl SymWrapper { - pub fn new(lib_paths: &[&str]) -> Result { + pub unsafe fn new(lib_paths: &[&str]) -> Result { unsafe { for path in lib_paths { #[cfg(windows)] diff --git a/glutin/src/platform/x11.rs b/glutin/src/platform/x11.rs index a34654d74a..6f8d6b3c02 100644 --- a/glutin/src/platform/x11.rs +++ b/glutin/src/platform/x11.rs @@ -1,4 +1,4 @@ -//! Dedicated utils to work with X11 shenanigans. +//! Utilities to access X11 specific config properties. use std::mem; @@ -14,7 +14,7 @@ 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 options out of the config. +/// The GlConfig extension trait to get X11 specific properties from a config. pub trait X11GlConfigExt { /// The `X11VisualInfo` that must be used to inititalize the Xlib window. fn x11_visual(&self) -> Option; @@ -39,11 +39,13 @@ impl X11VisualInfo { return None; } - let mut raw: XVisualInfo = std::mem::zeroed(); - raw.visualid = xid; + let raw = unsafe { + let mut raw: XVisualInfo = std::mem::zeroed(); + raw.visualid = xid; - let mut num_visuals = 0; - let raw = (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals); + let mut num_visuals = 0; + (xlib.XGetVisualInfo)(display, VisualIDMask, &mut raw, &mut num_visuals) + }; if raw.is_null() { return None; diff --git a/glutin/src/prelude.rs b/glutin/src/prelude.rs index 23d7244d0f..24dd3e77ad 100644 --- a/glutin/src/prelude.rs +++ b/glutin/src/prelude.rs @@ -1,7 +1,8 @@ //! The glutin prelude. //! -//! The purpose of this module is to bring common imports, given that all -//! graphics api are on the traits for the documetation sharing purposes. +//! 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)] diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs index 21a93b0308..ea5d582d78 100644 --- a/glutin/src/surface.rs +++ b/glutin/src/surface.rs @@ -58,10 +58,10 @@ pub trait GlSurface: Sealed { /// Set swap interval for the surface. /// - /// See the docs for [`crate::surface::SwapInterval`] on the details. + /// See [`crate::surface::SwapInterval`] for details. fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()>; - /// Resize the surface to the new size. + /// Resize the surface to a new size. /// /// This call is for compatibility reasons, on most platforms it's a no-op. /// @@ -107,7 +107,7 @@ impl SurfaceAttributesBuilder { /// /// # Api-specific. /// - /// This only controls EGL surfaces, since the rest are using context for + /// 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; @@ -125,7 +125,7 @@ impl SurfaceAttributesBuilder { /// /// # Api-specific. /// - /// This is EGL specific, since the rest are using it on the context. + /// 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 diff --git a/glutin_examples/examples/window.rs b/glutin_examples/examples/window.rs index 10281a3bfa..0b09a8afea 100644 --- a/glutin_examples/examples/window.rs +++ b/glutin_examples/examples/window.rs @@ -31,7 +31,7 @@ fn main() { let gl_display = create_display(raw_display, raw_window_handle); // Create the config we'll be used for window. We'll use the native window - // raw-window-handle for it to get the rigth visual and use proper hdc. Note + // raw-window-handle for it to get the right visual and use proper hdc. Note // that you can likely use it for other windows using the same config. let template = config_template(window.raw_window_handle()); let config = unsafe { gl_display.find_configs(template).unwrap().next().unwrap() }; From f63202c49d8aa250e084a88206dcdf3ca3dc9008 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 02:18:32 +0300 Subject: [PATCH 3/8] More doc fixes --- glutin/src/config.rs | 2 +- glutin/src/context.rs | 2 +- glutin/src/display.rs | 2 +- glutin/src/error.rs | 2 +- glutin/src/lib.rs | 29 ++++++++++++++--------------- glutin/src/platform/mod.rs | 2 +- glutin/src/surface.rs | 2 +- 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/glutin/src/config.rs b/glutin/src/config.rs index 1bfa0ed531..508b033c77 100644 --- a/glutin/src/config.rs +++ b/glutin/src/config.rs @@ -1,4 +1,4 @@ -//! GL config picking and creating utils. +//! Api config picking and creating utils. #![allow(unreachable_patterns)] use std::num::NonZeroU32; diff --git a/glutin/src/context.rs b/glutin/src/context.rs index f53ebccb35..a3269466af 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -1,4 +1,4 @@ -//! OpenGL context handling. +//! OpenGL context creation and initialization. #![allow(unreachable_patterns)] use std::ffi::{self, CStr}; diff --git a/glutin/src/display.rs b/glutin/src/display.rs index 5c95ed4a10..4ffcfec1d7 100644 --- a/glutin/src/display.rs +++ b/glutin/src/display.rs @@ -1,4 +1,4 @@ -//! The GL platform display creation and picking. +//! The OpenGL platform display selection and creation. #![allow(unreachable_patterns)] use std::fmt; diff --git a/glutin/src/error.rs b/glutin/src/error.rs index 7ea25f5698..f77052789e 100644 --- a/glutin/src/error.rs +++ b/glutin/src/error.rs @@ -1,4 +1,4 @@ -//! The catch all error used by glutin. +//! Glutin error handling. use std::fmt; diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs index 8662447141..c9e25bcb24 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -1,21 +1,20 @@ -//! The purpose of this library is to provide OpenGL `[context]` on as many -//! platforms as possible abstracting away the platform inconsistencies, -//! while providing direct access to them to make the use of platform specific -//! extensions when needed. +//! 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. //! -//! Glutin does not force to use the cross platform abstraction, -//! for example, when only `[EGL]` is desired, it can be used directly. +//! 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 underlying -//! graphics platform Api when creating a `[display]`. A display is used to -//! create all the common platform Api objects, such as `[config]`, `[context]`, -//! and `[surface]`. +//! 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`]. //! -//! [display]: crate::display -//! [context]: crate::context -//! [surface]: crate::surface -//! [config]: crate::config -//! [EGL]: crate::api::egl +//! [`display`]: crate::display +//! [`context`]: crate::context +//! [`surface`]: crate::surface +//! [`config`]: crate::config +//! [`Api`]: crate::api #![deny(rust_2018_idioms)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/glutin/src/platform/mod.rs b/glutin/src/platform/mod.rs index 47cc0d7292..8627aef8b5 100644 --- a/glutin/src/platform/mod.rs +++ b/glutin/src/platform/mod.rs @@ -1,4 +1,4 @@ -//! Platform specific utils to simplify interactions with the Api. +//! Platform-specific API helpers. #[cfg(x11_platform)] pub mod x11; diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs index ea5d582d78..db7015dca7 100644 --- a/glutin/src/surface.rs +++ b/glutin/src/surface.rs @@ -1,4 +1,4 @@ -//! A cross platform GL surface representation. +//! A cross platform OpenGL surface representation. #![allow(unreachable_patterns)] use std::marker::PhantomData; From 5e8dc572103c01535b5bbed6dc9bdb14d6af57f5 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 02:46:17 +0300 Subject: [PATCH 4/8] Tune config api reporting --- glutin/src/api/cgl/context.rs | 6 +++++- glutin/src/api/glx/config.rs | 8 +++++++- glutin/src/api/wgl/config.rs | 12 ++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/glutin/src/api/cgl/context.rs b/glutin/src/api/cgl/context.rs index 4c50aa5228..d19106096f 100644 --- a/glutin/src/api/cgl/context.rs +++ b/glutin/src/api/cgl/context.rs @@ -15,7 +15,7 @@ use objc::rc::autoreleasepool; use objc::runtime::{BOOL, NO}; use crate::config::GetGlConfig; -use crate::context::{AsRawContext, ContextAttributes, RawContext, Robustness}; +use crate::context::{AsRawContext, ContextApi, ContextAttributes, RawContext, Robustness}; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::prelude::*; @@ -37,6 +37,10 @@ impl Display { _ => nil, }; + if matches!(context_attributes.api, 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()); } diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index 32d73bce7e..4a39ceacef 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -268,7 +268,13 @@ impl GlConfig for Config { } fn api(&self) -> Api { - Api::OPENGL + let mut api = Api::OPENGL; + if self.inner.display.inner.client_extensions.contains("GLX_EXT_create_context_es2_profile") + { + api |= Api::GLES2; + } + + api } } diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index c1eec3d6f8..c759c0ccc3 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -342,7 +342,9 @@ impl GlConfig for Config { } fn srgb_capable(&self) -> bool { - if self.inner.display.inner.client_extensions.contains(SRGB_EXT) { + if self.inner.display.inner.client_extensions.contains(SRGB_EXT) + || self.inner.display.inner.client_extensions.contains("WGL_EXT_colorspace") + { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } else if self.inner.display.inner.client_extensions.contains(SRGB_ARB) { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 @@ -400,7 +402,13 @@ impl GlConfig for Config { } fn api(&self) -> Api { - Api::OPENGL + let mut api = Api::OPENGL; + if self.inner.display.inner.client_extensions.contains("WGL_EXT_create_context_es2_profile") + { + api |= Api::GLES2; + } + + api } } From 2c23bf3f9be51aaa04ab8be099e822cf2534e639 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 03:41:59 +0300 Subject: [PATCH 5/8] Refactor display picking --- CHANGELOG.md | 2 +- glutin/src/api/glx/display.rs | 4 +- glutin/src/config.rs | 4 +- glutin/src/display.rs | 194 ++++++++---------------- glutin_examples/Cargo.toml | 2 +- glutin_examples/examples/support/mod.rs | 30 ++-- 6 files changed, 84 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 230ba3f011..552bc40d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - The ios support was removed for the lack of maintainance for now. In case there's a need for it, contributions are welcome. - The context creation is no longer limited to winit's supported platforms. - The underlying Api providers are publically exposed now, so glutin could be used with just e.g. `EGL`. -- Fixed soundness issues with `Surface` MT safety, since before `EGLSurface` could be sent to a different thread, wich is not safe. +- Fixed soundness issues with `Surface` MT safety, since before `EGLSurface` could be sent to a different thread, which is not safe. # Version 0.29.1 (2022-08-10) diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs index 4c1837cd61..4581baa622 100644 --- a/glutin/src/api/glx/display.rs +++ b/glutin/src/api/glx/display.rs @@ -35,7 +35,9 @@ impl Display { /// /// # Safety /// - /// The `display` must point to the valid Xlib display. + /// 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 from_raw( display: RawDisplayHandle, error_hook_registrar: XlibErrorHookRegistrar, diff --git a/glutin/src/config.rs b/glutin/src/config.rs index 508b033c77..a2e84addd2 100644 --- a/glutin/src/config.rs +++ b/glutin/src/config.rs @@ -176,8 +176,8 @@ impl ConfigTemplateBuilder { /// /// # Api-specific /// - /// EGL on X11 doesn't provide a way to create a transparent surface. Use - /// GLX for that instead. + /// 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; diff --git a/glutin/src/display.rs b/glutin/src/display.rs index 4ffcfec1d7..6ce274308b 100644 --- a/glutin/src/display.rs +++ b/glutin/src/display.rs @@ -161,61 +161,57 @@ impl Display { /// The `display` must point to the valid platform display and be valid for /// the entire lifetime of all Objects created with that display. /// - /// The `picker` must contain pointers to the valid values if GLX or WGL + /// The `preference` must contain pointers to the valid values if GLX or WGL /// specific options were used. - pub unsafe fn from_raw(display: RawDisplayHandle, picker: DisplayPicker) -> Result { - #[cfg(glx_backend)] - let registrar = picker - .glx_error_registrar - .expect("glx was requested, but error hook registrar wasn't provided."); - - match picker.api_preference { + pub unsafe fn from_raw( + display: RawDisplayHandle, + preference: DisplayApiPreference, + ) -> Result { + match preference { #[cfg(egl_backend)] DisplayApiPreference::Egl => unsafe { Ok(Self::Egl(EglDisplay::from_raw(display)?)) }, #[cfg(glx_backend)] - DisplayApiPreference::Glx => unsafe { + DisplayApiPreference::Glx(registrar) => unsafe { Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)) }, - #[cfg(wgl_backend)] - DisplayApiPreference::Wgl => unsafe { - Ok(Self::Wgl(WglDisplay::from_raw(display, picker.window_handle)?)) + #[cfg(all(egl_backend, glx_backend))] + DisplayApiPreference::GlxThenEgl(registrar) => unsafe { + if let Ok(display) = GlxDisplay::from_raw(display, registrar) { + Ok(Self::Glx(display)) + } else { + Ok(Self::Egl(EglDisplay::from_raw(display)?)) + } }, - #[cfg(cgl_backend)] - DisplayApiPreference::Cgl => Ok(Self::Cgl(CglDisplay::from_raw(display)?)), - #[cfg(all(egl_backend, glx_backend))] - DisplayApiPreference::EglThenGlx => unsafe { + DisplayApiPreference::EglThenGlx(registrar) => unsafe { if let Ok(display) = EglDisplay::from_raw(display) { Ok(Self::Egl(display)) } else { Ok(Self::Glx(GlxDisplay::from_raw(display, registrar)?)) } }, - #[cfg(all(egl_backend, glx_backend))] - DisplayApiPreference::GlxThenEgl => unsafe { - if let Ok(display) = GlxDisplay::from_raw(display, registrar) { - Ok(Self::Glx(display)) - } else { - Ok(Self::Egl(EglDisplay::from_raw(display)?)) - } + #[cfg(wgl_backend)] + DisplayApiPreference::Wgl(window_handle) => unsafe { + Ok(Self::Wgl(WglDisplay::from_raw(display, window_handle)?)) }, - #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::EglThenWgl => unsafe { + DisplayApiPreference::EglThenWgl(window_handle) => unsafe { if let Ok(display) = EglDisplay::from_raw(display) { Ok(Self::Egl(display)) } else { - Ok(Self::Wgl(WglDisplay::from_raw(display, picker.window_handle)?)) + Ok(Self::Wgl(WglDisplay::from_raw(display, window_handle)?)) } }, #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::WglThenEgl => unsafe { - if let Ok(display) = WglDisplay::from_raw(display, picker.window_handle) { + DisplayApiPreference::WglThenEgl(window_handle) => unsafe { + if let Ok(display) = WglDisplay::from_raw(display, window_handle) { Ok(Self::Wgl(display)) } else { Ok(Self::Egl(EglDisplay::from_raw(display)?)) } }, + #[cfg(cgl_backend)] + DisplayApiPreferences::Cgl => unsafe { Ok(Self::Cgl(CglDisplay::from_raw(display)?)) }, } } } @@ -364,131 +360,65 @@ impl AsRawDisplay for Display { impl Sealed for Display {} -/// Settings to control automatic display picking. -pub struct DisplayPicker { - pub(crate) api_preference: DisplayApiPreference, - #[cfg(glx_backend)] - pub(crate) glx_error_registrar: Option, - #[cfg(wgl_backend)] - pub(crate) window_handle: Option, -} - -impl DisplayPicker { - /// Create a default display picker. - pub fn new() -> Self { - Default::default() - } - - /// The preference of the underlying system Api. - /// - /// For platforms supporting `EGL` the default is `EGL` given that other api - /// providers require extra options to get them work. - pub fn with_api_preference(mut self, api_preference: DisplayApiPreference) -> Self { - self.api_preference = api_preference; - self - } - - /// Create WGL display which will be the most suitable to operate with the - /// given window. - /// - /// When the window isn't provided the support for extensions and pixel - /// formats may be lacking. - #[cfg(wgl_backend)] - pub fn with_most_compatible_for_window( - mut self, - window_handle: raw_window_handle::RawWindowHandle, - ) -> Self { - self.window_handle = Some(window_handle); - self - } - - /// The hook to register glutin error handler in X11 error handling - /// function. - /// - /// The hook registrar must be provided in case GLX will be used. - #[cfg(glx_backend)] - pub fn with_glx_error_registrar(mut self, error_registrar: XlibErrorHookRegistrar) -> Self { - self.glx_error_registrar = Some(error_registrar); - self - } -} - -impl Default for DisplayPicker { - #[cfg(all(egl_backend, glx_backend))] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::Egl, glx_error_registrar: None } - } - - #[cfg(all(egl_backend, not(glx_backend), not(wgl_backend)))] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::Egl } - } - - #[cfg(all(glx_backend, not(egl_backend)))] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::Glx, glx_error_registrar: None } - } - - #[cfg(all(wgl_backend, not(egl_backend)))] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::Wgl, window_handle: None } - } - - #[cfg(all(wgl_backend, egl_backend))] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::WglThenEgl, window_handle: None } - } - - #[cfg(cgl_backend)] - fn default() -> Self { - Self { api_preference: DisplayApiPreference::Cgl } - } -} - -impl fmt::Debug for DisplayPicker { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = f.debug_struct("DisplayPicker"); - let f = f.field("api_preference", &self.api_preference); - - #[cfg(glx_backend)] - let f = f.field("glx_registrar", &self.glx_error_registrar.is_some()); - - #[cfg(wgl_backend)] - let f = f.field("window_handle", &self.window_handle); - - f.finish() - } -} - /// Preference of the display that should be used. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DisplayApiPreference { /// Prefer EGL. #[cfg(egl_backend)] Egl, /// Prefer GLX. + /// + /// The hook to register glutin error handler in the X11 error handling + /// function. #[cfg(glx_backend)] - Glx, + Glx(XlibErrorHookRegistrar), /// Prefer WGL. + /// + /// When raw window handle isn't provided the display will lack extensions + /// support and most features will be lacking. #[cfg(wgl_backend)] - Wgl, + Wgl(Option), /// Prefer CGL. #[cfg(cgl_backend)] Cgl, /// Prefer EGL and fallback to GLX. #[cfg(all(egl_backend, glx_backend))] - EglThenGlx, + EglThenGlx(XlibErrorHookRegistrar), /// Prefer GLX and fallback to EGL. #[cfg(all(egl_backend, glx_backend))] - GlxThenEgl, + GlxThenEgl(XlibErrorHookRegistrar), /// Prefer EGL and fallback to GLX. #[cfg(all(egl_backend, wgl_backend))] - EglThenWgl, - /// Prefer GLX and fallback to EGL. + EglThenWgl(Option), + /// Prefer WGL and fallback to EGL. #[cfg(all(egl_backend, wgl_backend))] - WglThenEgl, + 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)) + } } /// Raw GL platform display. diff --git a/glutin_examples/Cargo.toml b/glutin_examples/Cargo.toml index 0ea01e7540..fe637520d6 100644 --- a/glutin_examples/Cargo.toml +++ b/glutin_examples/Cargo.toml @@ -13,7 +13,7 @@ publish = false [features] default = ["egl", "glx", "x11", "wayland", "wgl"] egl = ["glutin/egl"] -glx = ["glutin/glx", "glutin/x11", "winit/x11"] +glx = ["glutin/glx", "glutin/x11", "winit/x11", "x11"] wgl = ["glutin/wgl"] x11 = ["glutin/x11", "winit/x11"] wayland = ["glutin/wayland", "winit/wayland", "winit/wayland-dlopen", "winit/wayland-csd-adwaita-notitle"] diff --git a/glutin_examples/examples/support/mod.rs b/glutin_examples/examples/support/mod.rs index fdb1de5acb..0edcaca7a4 100644 --- a/glutin_examples/examples/support/mod.rs +++ b/glutin_examples/examples/support/mod.rs @@ -12,7 +12,7 @@ use winit::platform::unix; use winit::window::{Window, WindowBuilder}; use glutin::config::{Config, ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder}; -use glutin::display::{Display, DisplayPicker}; +use glutin::display::{Display, DisplayApiPreference}; use glutin::prelude::*; use glutin::surface::{Surface, SurfaceAttributes, SurfaceAttributesBuilder, WindowSurface}; @@ -68,24 +68,24 @@ pub fn create_display( raw_display: RawDisplayHandle, raw_window_handle: RawWindowHandle, ) -> Display { - #[cfg(not(all(glx_backend, wgl_backend)))] - let picker = DisplayPicker::new(); + #[cfg(egl_backend)] + let preference = DisplayApiPreference::Egl; + + #[cfg(glx_backend)] + let preference = DisplayApiPreference::Glx(Box::new(unix::register_xlib_error_hook)); + + #[cfg(cgl_backend)] + let preference = DisplayApiPreference::Cgl; + + #[cfg(wgl_backend)] + let preference = DisplayApiPreference::Wgl(Some(raw_window_handle)); #[cfg(all(egl_backend, wgl_backend))] - let picker = DisplayPicker::new() - .with_most_compatible_for_window(raw_window_handle) - .with_api_preference(glutin::display::DisplayApiPreference::WglThenEgl); + let preference = DisplayApiPreference::WglThenEgl(Some(raw_window_handle)); #[cfg(all(egl_backend, glx_backend))] - let picker = DisplayPicker::new() - .with_api_preference(glutin::display::DisplayApiPreference::GlxThenEgl) - .with_glx_error_registrar(Box::new(unix::register_xlib_error_hook)); - - #[cfg(all(glx_backend, not(egl_backend)))] - let picker = DisplayPicker::new() - .with_api_preference(glutin::display::DisplayApiPreference::Glx) - .with_glx_error_registrar(Box::new(unix::register_xlib_error_hook)); + let preference = DisplayApiPreference::GlxThenEgl(Box::new(unix::register_xlib_error_hook)); // Create connection to underlying OpenGL client Api. - unsafe { Display::from_raw(raw_display, picker).unwrap() } + unsafe { Display::from_raw(raw_display, preference).unwrap() } } From 2d65e0817aef30f8470c303a4bd9a97be066625b Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 14:39:29 +0300 Subject: [PATCH 6/8] Handle original xcb extension --- glutin/src/api/egl/display.rs | 5 ++++- glutin/src/api/glx/config.rs | 4 ++-- glutin/src/api/glx/surface.rs | 2 +- glutin/src/api/wgl/config.rs | 2 +- glutin/src/display.rs | 8 ++++---- glutin/src/error.rs | 3 ++- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index e8b3d5f16d..bb4530cd49 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -164,7 +164,10 @@ impl Display { (egl::PLATFORM_X11_EXT, handle.display) }, #[cfg(x11_platform)] - RawDisplayHandle::Xcb(handle) if extensions.contains("EGL_MESA_platform_xcb") => { + RawDisplayHandle::Xcb(handle) + if extensions.contains("EGL_MESA_platform_xcb") + || extensions.contains("EGL_EXT_platform_xcb") => + { attrs.push(egl::PLATFORM_XCB_EXT as EGLint); attrs.push(handle.screen as EGLint); (egl::PLATFORM_XCB_EXT, handle.connection) diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index 4a39ceacef..cdddfaa15c 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -193,7 +193,7 @@ impl Config { } } - pub(crate) fn is_singe_buffered(&self) -> bool { + pub(crate) fn is_single_buffered(&self) -> bool { self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 } } @@ -271,7 +271,7 @@ impl GlConfig for Config { let mut api = Api::OPENGL; if self.inner.display.inner.client_extensions.contains("GLX_EXT_create_context_es2_profile") { - api |= Api::GLES2; + api |= Api::GLES1 | Api::GLES2; } api diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs index 4be85e8bcc..9fc21d3063 100644 --- a/glutin/src/api/glx/surface.rs +++ b/glutin/src/api/glx/surface.rs @@ -205,7 +205,7 @@ impl GlSurface for Surface { } fn is_single_buffered(&self) -> bool { - self.config.is_singe_buffered() + self.config.is_single_buffered() } fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index c759c0ccc3..fd342376f0 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -405,7 +405,7 @@ impl GlConfig for Config { let mut api = Api::OPENGL; if self.inner.display.inner.client_extensions.contains("WGL_EXT_create_context_es2_profile") { - api |= Api::GLES2; + api |= Api::GLES1 | Api::GLES2; } api diff --git a/glutin/src/display.rs b/glutin/src/display.rs index 6ce274308b..5a3d677329 100644 --- a/glutin/src/display.rs +++ b/glutin/src/display.rs @@ -211,7 +211,7 @@ impl Display { } }, #[cfg(cgl_backend)] - DisplayApiPreferences::Cgl => unsafe { Ok(Self::Cgl(CglDisplay::from_raw(display)?)) }, + DisplayApiPreference::Cgl => unsafe { Ok(Self::Cgl(CglDisplay::from_raw(display)?)) }, } } } @@ -408,11 +408,11 @@ impl fmt::Debug for DisplayApiPreference { #[cfg(all(egl_backend, glx_backend))] DisplayApiPreference::EglThenGlx(_) => "EglThenGlx", #[cfg(wgl_backend)] - DisplayApiPreference::Wgl => "Wgl", + DisplayApiPreference::Wgl(_) => "Wgl", #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::EglThenWgl => "EglThenWgl", + DisplayApiPreference::EglThenWgl(_) => "EglThenWgl", #[cfg(all(egl_backend, wgl_backend))] - DisplayApiPreference::WglThenEgl => "WglThenEgl", + DisplayApiPreference::WglThenEgl(_) => "WglThenEgl", #[cfg(cgl_backend)] DisplayApiPreference::Cgl => "Cgl", }; diff --git a/glutin/src/error.rs b/glutin/src/error.rs index f77052789e..a000c3a846 100644 --- a/glutin/src/error.rs +++ b/glutin/src/error.rs @@ -76,7 +76,8 @@ impl From for Error { /// errors. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum ErrorKind { - /// The requested resource wasn't found. + /// The requested display wasn't found or some required symbol in it was + /// missing. NotFound, /// Failed to perform resource initialization. From 955c7be987289fe9d5698f8605e52c17e776b491 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 20:30:03 +0300 Subject: [PATCH 7/8] Add ability to pick hardware accelerated configs --- glutin/src/api/cgl/config.rs | 4 ++++ glutin/src/api/cgl/display.rs | 2 +- glutin/src/api/egl/config.rs | 10 ++++++++++ glutin/src/api/glx/config.rs | 10 ++++++++++ glutin/src/api/wgl/config.rs | 16 ++++++++++++++++ glutin/src/config.rs | 13 +++++++++++++ 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs index aa28a70700..f19b0d3dc6 100644 --- a/glutin/src/api/cgl/config.rs +++ b/glutin/src/api/cgl/config.rs @@ -70,6 +70,10 @@ impl Display { attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFADoubleBuffer as u32); } + if template.hardware_accelerated == Some(true) { + attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAAccelerated as u32); + } + // Stereo. if template.stereoscopy == Some(true) { attrs.push(NSOpenGLPixelFormatAttribute::NSOpenGLPFAStereo as u32); diff --git a/glutin/src/api/cgl/display.rs b/glutin/src/api/cgl/display.rs index 876ca090af..cd5926eac8 100644 --- a/glutin/src/api/cgl/display.rs +++ b/glutin/src/api/cgl/display.rs @@ -24,7 +24,7 @@ pub struct Display { impl Display { /// Create CGL display. - pub fn from_raw(display: RawDisplayHandle) -> Result { + pub unsafe fn from_raw(display: RawDisplayHandle) -> Result { match display { RawDisplayHandle::AppKit(..) => Ok(Display { _marker: PhantomData }), _ => Err(ErrorKind::NotSupported("provided native display is not supported").into()), diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs index 11fe569ab3..986759949c 100644 --- a/glutin/src/api/egl/config.rs +++ b/glutin/src/api/egl/config.rs @@ -93,6 +93,16 @@ impl Display { } 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); diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index cdddfaa15c..958fe2ca0d 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -70,6 +70,16 @@ impl Display { 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); diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index fd342376f0..b4d2aa0ef4 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -83,6 +83,13 @@ impl Display { 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. @@ -212,6 +219,15 @@ impl Display { 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); diff --git a/glutin/src/config.rs b/glutin/src/config.rs index a2e84addd2..73f3feb27a 100644 --- a/glutin/src/config.rs +++ b/glutin/src/config.rs @@ -192,6 +192,15 @@ impl ConfigTemplateBuilder { self } + /// Wether 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 @@ -277,6 +286,9 @@ pub struct ConfigTemplate { /// 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, @@ -315,6 +327,7 @@ impl Default for ConfigTemplate { max_pbuffer_height: None, native_window: None, + hardware_accelerated: None, api: Api::OPENGL, } From 2f1f94b7530775208d969a145b31a2f7ba555bd8 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 30 Aug 2022 22:16:53 +0300 Subject: [PATCH 8/8] Mark raw attributes accessors as unsafe --- glutin/src/api/cgl/config.rs | 4 +-- glutin/src/api/cgl/display.rs | 4 +++ glutin/src/api/egl/config.rs | 63 +++++++++++++++++++---------------- glutin/src/api/egl/surface.rs | 18 +++++++--- glutin/src/api/glx/config.rs | 58 +++++++++++++++++++------------- glutin/src/api/glx/surface.rs | 16 ++++++--- glutin/src/api/wgl/config.rs | 40 ++++++++++++++-------- glutin/src/config.rs | 6 ++-- 8 files changed, 131 insertions(+), 78 deletions(-) diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs index f19b0d3dc6..385986e686 100644 --- a/glutin/src/api/cgl/config.rs +++ b/glutin/src/api/cgl/config.rs @@ -130,7 +130,7 @@ impl Config { } impl GlConfig for Config { - fn color_buffer_type(&self) -> ColorBufferType { + 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. @@ -139,7 +139,7 @@ impl GlConfig for Config { 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; - ColorBufferType::Rgb { r_size, g_size, b_size } + Some(ColorBufferType::Rgb { r_size, g_size, b_size }) } fn float_pixels(&self) -> bool { diff --git a/glutin/src/api/cgl/display.rs b/glutin/src/api/cgl/display.rs index cd5926eac8..73dae4e02d 100644 --- a/glutin/src/api/cgl/display.rs +++ b/glutin/src/api/cgl/display.rs @@ -24,6 +24,10 @@ pub struct Display { impl Display { /// Create CGL display. + /// + /// # Safety + /// + /// The function is unsafe for consistency. pub unsafe fn from_raw(display: RawDisplayHandle) -> Result { match display { RawDisplayHandle::AppKit(..) => Ok(Display { _marker: PhantomData }), diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs index 986759949c..d63d25fd39 100644 --- a/glutin/src/api/egl/config.rs +++ b/glutin/src/api/egl/config.rs @@ -219,10 +219,13 @@ impl Config { /// The interpretation of this value is platform dependant. Consult /// `platform` extension you're ended up using. pub fn native_visual(&self) -> u32 { - self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 + unsafe { self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 } } - fn raw_attribute(&self, attr: EGLint) -> EGLint { + /// # 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( @@ -237,35 +240,39 @@ impl Config { } impl GlConfig for Config { - fn color_buffer_type(&self) -> ColorBufferType { - match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ { - egl::LUMINANCE_BUFFER => { - let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint); - ColorBufferType::Luminance(luma as u8) - }, - egl::RGB_BUFFER => { - let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8; - let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8; - let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8; - ColorBufferType::Rgb { r_size, g_size, b_size } - }, - _ => unreachable!(), + fn 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 { - if self.inner.display.inner.client_extensions.contains(FLOAT_PIXELS_EXT) { - matches!( - self.raw_attribute(egl::COLOR_COMPONENT_TYPE_EXT as EGLint) as _, - egl::COLOR_COMPONENT_TYPE_FLOAT_EXT - ) - } else { - false + unsafe { + if self.inner.display.inner.client_extensions.contains(FLOAT_PIXELS_EXT) { + 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 { - self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 + unsafe { self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 } } fn srgb_capable(&self) -> bool { @@ -273,21 +280,21 @@ impl GlConfig for Config { } fn depth_size(&self) -> u8 { - self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 + unsafe { self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 } } fn stencil_size(&self) -> u8 { - self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 + unsafe { self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 } } fn sample_buffers(&self) -> u8 { - self.raw_attribute(egl::SAMPLE_BUFFERS as EGLint) as u8 + unsafe { self.raw_attribute(egl::SAMPLE_BUFFERS as EGLint) as u8 } } fn config_surface_types(&self) -> ConfigSurfaceTypes { let mut ty = ConfigSurfaceTypes::empty(); - let raw_ty = self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32; + 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); } @@ -303,7 +310,7 @@ impl GlConfig for Config { fn api(&self) -> Api { let mut api = Api::empty(); - let raw_api = self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32; + 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); } diff --git a/glutin/src/api/egl/surface.rs b/glutin/src/api/egl/surface.rs index 46397f5096..70fbbf5598 100644 --- a/glutin/src/api/egl/surface.rs +++ b/glutin/src/api/egl/surface.rs @@ -263,7 +263,10 @@ impl Surface { } } - fn raw_attribute(&self, attr: EGLint) -> EGLint { + /// # 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( @@ -290,19 +293,24 @@ impl GlSurface for Surface { type SurfaceType = T; fn buffer_age(&self) -> u32 { - self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) as u32 + self.display + .inner + .client_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 { - Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + unsafe { Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) } } fn height(&self) -> Option { - Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) + unsafe { Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) } } fn is_single_buffered(&self) -> bool { - self.raw_attribute(egl::RENDER_BUFFER as EGLint) != egl::SINGLE_BUFFER as i32 + unsafe { self.raw_attribute(egl::RENDER_BUFFER as EGLint) != egl::SINGLE_BUFFER as i32 } } fn swap_buffers(&self, _context: &Self::Context) -> Result<()> { diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index 958fe2ca0d..aa89799e4e 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -190,7 +190,10 @@ pub struct Config { } impl Config { - fn raw_attribute(&self, attr: c_int) -> c_int { + /// # 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( @@ -204,30 +207,33 @@ impl Config { } pub(crate) fn is_single_buffered(&self) -> bool { - self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 + unsafe { self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 } } } impl GlConfig for Config { - fn color_buffer_type(&self) -> ColorBufferType { - match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ { - glx::TRUE_COLOR => { - let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8; - let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8; - let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8; - ColorBufferType::Rgb { r_size, g_size, b_size } - }, - glx::GRAY_SCALE => { - let luma = self.raw_attribute(glx::RED_SIZE as c_int); - ColorBufferType::Luminance(luma as u8) - }, - _ => unimplemented!(), + fn 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.client_extensions.contains(FLOAT_PIXEL_EXT) { - let render_type = self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum; + 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 @@ -235,35 +241,35 @@ impl GlConfig for Config { } fn alpha_size(&self) -> u8 { - self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 + unsafe { self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 } } fn srgb_capable(&self) -> bool { if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { - self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 + 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") { - self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } } else { false } } fn depth_size(&self) -> u8 { - self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 + unsafe { self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 } } fn stencil_size(&self) -> u8 { - self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 + unsafe { self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 } } fn sample_buffers(&self) -> u8 { - self.raw_attribute(glx::SAMPLE_BUFFERS as c_int) as u8 + unsafe { self.raw_attribute(glx::SAMPLE_BUFFERS as c_int) as u8 } } fn config_surface_types(&self) -> ConfigSurfaceTypes { let mut ty = ConfigSurfaceTypes::empty(); - let raw_ty = self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32; + 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); } @@ -280,6 +286,12 @@ impl GlConfig for Config { fn api(&self) -> Api { let mut api = Api::OPENGL; if self.inner.display.inner.client_extensions.contains("GLX_EXT_create_context_es2_profile") + || self + .inner + .display + .inner + .client_extensions + .contains("GLX_EXT_create_context_es_profile") { api |= Api::GLES1 | Api::GLES2; } diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs index 9fc21d3063..8f7302a45d 100644 --- a/glutin/src/api/glx/surface.rs +++ b/glutin/src/api/glx/surface.rs @@ -154,7 +154,10 @@ pub struct Surface { } impl Surface { - fn raw_attribute(&self, attr: c_int) -> c_uint { + /// # 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 @@ -193,15 +196,20 @@ impl GlSurface for Surface { type SurfaceType = T; fn buffer_age(&self) -> u32 { - self.raw_attribute(glx_extra::BACK_BUFFER_AGE_EXT as c_int) as u32 + 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 { - Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + unsafe { Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) } } fn height(&self) -> Option { - Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) + unsafe { Some(self.raw_attribute(glx::HEIGHT as c_int) as u32) } } fn is_single_buffered(&self) -> bool { diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index b4d2aa0ef4..1a58d779ec 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -309,11 +309,14 @@ impl Config { pub(crate) fn is_single_buffered(&self) -> bool { match self.inner.descriptor.as_ref() { Some(descriptor) => (descriptor.dwFlags & gl::PFD_DOUBLEBUFFER) == 0, - None => self.raw_attribute(wgl_extra::DOUBLE_BUFFER_ARB as c_int) == 0, + None => unsafe { self.raw_attribute(wgl_extra::DOUBLE_BUFFER_ARB as c_int) == 0 }, } } - fn raw_attribute(&self, attr: c_int) -> c_int { + /// # 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; @@ -331,10 +334,10 @@ impl Config { } impl GlConfig for Config { - fn color_buffer_type(&self) -> ColorBufferType { + 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; @@ -342,18 +345,21 @@ impl GlConfig for Config { }, }; - ColorBufferType::Rgb { r_size, g_size, b_size } + Some(ColorBufferType::Rgb { r_size, g_size, b_size }) } fn float_pixels(&self) -> bool { - self.raw_attribute(wgl_extra::PIXEL_TYPE_ARB as c_int) - == wgl_extra::TYPE_RGBA_FLOAT_ARB as c_int + unsafe { + self.inner.display.inner.client_extensions.contains("WGL_ARB_pixel_format_float") + && 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, - _ => self.raw_attribute(wgl_extra::ALPHA_BITS_ARB as c_int) as _, + _ => unsafe { self.raw_attribute(wgl_extra::ALPHA_BITS_ARB as c_int) as _ }, } } @@ -361,9 +367,9 @@ impl GlConfig for Config { if self.inner.display.inner.client_extensions.contains(SRGB_EXT) || self.inner.display.inner.client_extensions.contains("WGL_EXT_colorspace") { - self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 + 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) { - self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 + unsafe { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } } else { false } @@ -372,20 +378,20 @@ impl GlConfig for Config { fn depth_size(&self) -> u8 { match self.inner.descriptor.as_ref() { Some(descriptor) => descriptor.cDepthBits, - _ => self.raw_attribute(wgl_extra::DEPTH_BITS_ARB as c_int) as _, + _ => 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, - _ => self.raw_attribute(wgl_extra::STENCIL_BITS_ARB as c_int) as _, + _ => unsafe { self.raw_attribute(wgl_extra::STENCIL_BITS_ARB as c_int) as _ }, } } fn sample_buffers(&self) -> u8 { if self.inner.display.inner.client_extensions.contains(MULTI_SAMPLE_ARB) { - self.raw_attribute(wgl_extra::SAMPLES_ARB as c_int) as _ + unsafe { self.raw_attribute(wgl_extra::SAMPLES_ARB as c_int) as _ } } else { 0 } @@ -404,7 +410,7 @@ impl GlConfig for Config { flags |= ConfigSurfaceTypes::PIXMAP; } }, - _ => { + _ => unsafe { if self.raw_attribute(wgl_extra::DRAW_TO_WINDOW_ARB as c_int) != 0 { flags |= ConfigSurfaceTypes::WINDOW } @@ -420,6 +426,12 @@ impl GlConfig for Config { fn api(&self) -> Api { let mut api = Api::OPENGL; if self.inner.display.inner.client_extensions.contains("WGL_EXT_create_context_es2_profile") + || self + .inner + .display + .inner + .client_extensions + .contains("WGL_EXT_create_context_es_profile") { api |= Api::GLES1 | Api::GLES2; } diff --git a/glutin/src/config.rs b/glutin/src/config.rs index 73f3feb27a..bec69f0be9 100644 --- a/glutin/src/config.rs +++ b/glutin/src/config.rs @@ -24,7 +24,9 @@ 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. - fn color_buffer_type(&self) -> ColorBufferType; + /// + /// `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; @@ -416,7 +418,7 @@ pub enum Config { } impl GlConfig for Config { - fn color_buffer_type(&self) -> ColorBufferType { + fn color_buffer_type(&self) -> Option { gl_api_dispatch!(self; Self(config) => config.color_buffer_type()) }