diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/khronos-egl | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/khronos-egl')
-rw-r--r-- | third_party/rust/khronos-egl/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/CHANGELOG.md | 58 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/Cargo.lock | 275 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/Cargo.toml | 68 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/LICENSE-MIT | 23 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/README.md | 165 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/build.rs | 12 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/examples/load-minimal.rs | 43 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/examples/wayland-dynamic.rs | 348 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/examples/wayland-static.rs | 344 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/shell-wayland.nix | 11 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/shell.nix | 10 | ||||
-rw-r--r-- | third_party/rust/khronos-egl/src/lib.rs | 2363 |
14 files changed, 3922 insertions, 0 deletions
diff --git a/third_party/rust/khronos-egl/.cargo-checksum.json b/third_party/rust/khronos-egl/.cargo-checksum.json new file mode 100644 index 0000000000..a0593b0708 --- /dev/null +++ b/third_party/rust/khronos-egl/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"72e6058dddb110c7e70fb26e2f370b9958a5e03efdea048f62fb338807fbe7de","Cargo.lock":"99216196a89fb759029d053bdc1c32b4423295793a0f4dc5f4218626e7a29c21","Cargo.toml":"efbaf25a93c000f823abe977e272de2d43a35b1cfcbf0fc03d2df00631251871","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"613a096d04cd5ae2ab67acd09298ea30c7d7e89eabcc817249ea9b88f88470ae","build.rs":"54e6dd50d7b9f189b687b5f39b58131090004130a5d054614d3304a3912af96a","examples/load-minimal.rs":"a9bd9a34e2a30d189f878c5c2c1111e76c2fda70b10c284cf731c8cbfc921891","examples/wayland-dynamic.rs":"a7b32ca80fd124f4cf8cb7e6f2de6b0ca277f6619a5376b12e904c2ec1cc5a06","examples/wayland-static.rs":"b3f53b9ae1e5e639c9ee15485d16bd718cfd40f70635166318a224e337cee577","shell-wayland.nix":"ee9753492f0a7466b59ec23dff1c6d5c7a8178ec3dc1dd488964f1cb4859c44b","shell.nix":"1089d919bcbdf4afcd6f4403cfbe08a59253215398fdde34e6a4c62f70129f47","src/lib.rs":"7287d03b23a710f47f8cdaf2a82ed8a1c40af10737fb5a972a5fdf0600ef0b47"},"package":"8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"}
\ No newline at end of file diff --git a/third_party/rust/khronos-egl/CHANGELOG.md b/third_party/rust/khronos-egl/CHANGELOG.md new file mode 100644 index 0000000000..25b60e0986 --- /dev/null +++ b/third_party/rust/khronos-egl/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unpublished] +## [4.1.0] +### Changed +- `load_required` and `load` now trying to load `libEGL.so.1` or `libEGL.so`. + +## [4.0.0] +### Added +- `no-pkg-config` feature. +### Changed +- Upgrade dependency `libloading`: ^0.6 -> ^0.7. +### Removed +- `nightly` feature hich is no longer needed since `const_fn` is stabilized. + +## [3.0.2] +### Changed +- One Linux, use the `RTLD_NODELETE` when loading the EGL library in `load_required_from_filename` and `load_from_filename`. + +## [3.0.1] +### Changed +- Load `libEGL.so.1` by default instead of `libEGL.so`. + +## [3.0.0] +### Changed +- Impl `Debug` for `Static`, `Dynamic` and `Instance`. +- Add a `DynamicInstance` type alias for `Instance<Dynamic<libloading::Library>>` with helper functions. +- Precise version selection. +- Dynamic cast between versions with `Dynamic::load`, `Dynamic::load_required` and the `Upcast`/`Downcast` traits. +- `DynamicInstance::downcast` and `IDynamicInstance::upcast`. + +## [3.0.0-beta] +### Changed +- Removed the `khronos` dependency. +- Dynamic linking: Add the `Api` trait and the `Instance` struct along with the `static` and `dynamic` features. +- The dependency to `pkg-config` is now optional, only required by the `static` feature. +- Add an optional dependency to `libloading`, only required by the `dynamic` feature. + +## [2.2.0] +### Added +- Fix #9: new function `get_config_count` to get the number of available frame buffer configurations. + +## [2.1.1] +### Changed +- Upgrade dependency `gl`: ^0.11 -> ^0.14 +- Upgrade dependency `wayland-client`: ^0.23 -> ^0.25 +- Upgrade dependency `wayland-protocols`: ^0.23 -> ^0.25 +- Upgrade dependency `wayland-egl`: ^0.23 -> ^0.25 + +## [2.1.0] +### Changed +- Fix #3: accept `Option<Display>` instead of `Display` in `query_string`. +- More flexible dependencies versions. diff --git a/third_party/rust/khronos-egl/Cargo.lock b/third_party/rust/khronos-egl/Cargo.lock new file mode 100644 index 0000000000..18dabf048c --- /dev/null +++ b/third_party/rust/khronos-egl/Cargo.lock @@ -0,0 +1,275 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "dlib" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" +dependencies = [ + "libloading 0.6.7", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "khronos-egl" +version = "4.1.0" +dependencies = [ + "gl", + "libc", + "libloading 0.7.0", + "pkg-config", + "wayland-client", + "wayland-egl", + "wayland-protocols", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad167a2f54e832b82dbe003a046280dceffe5227b5f79e08e363a29638cfddd" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "wayland-client" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbdbe01d03b2267809f3ed99495b37395387fde789e0f2ebb78e8b43f75b6d7" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480450f76717edd64ad04a4426280d737fc3d10a236b982df7b1aee19f0e2d56" +dependencies = [ + "nix", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-egl" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c653507447113c967a1aeee413699acb42d96d6302ec967c6d51930eae8aa7f5" +dependencies = [ + "wayland-client", + "wayland-sys", +] + +[[package]] +name = "wayland-protocols" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319a82b4d3054dd25acc32d9aee0f84fa95b63bc983fffe4703b6b8d47e01a30" +dependencies = [ + "bitflags", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7010ba5767b3fcd350decc59055390b4ebe6bd1b9279a9feb1f1888987f1133d" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6793834e0c35d11fd96a97297abe03d37be627e1847da52e17d7e0e3b51cc099" +dependencies = [ + "dlib", + "pkg-config", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xml-rs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" diff --git a/third_party/rust/khronos-egl/Cargo.toml b/third_party/rust/khronos-egl/Cargo.toml new file mode 100644 index 0000000000..544cd386f5 --- /dev/null +++ b/third_party/rust/khronos-egl/Cargo.toml @@ -0,0 +1,68 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "khronos-egl" +version = "4.1.0" +authors = ["Timothée Haudebourg <author@haudebourg.net>", "Sean Kerr <sean@metatomic.io>"] +build = "build.rs" +description = "Rust bindings for EGL" +documentation = "https://docs.rs/khronos-egl" +readme = "README.md" +keywords = ["egl", "gl", "khronos", "opengl"] +license = "MIT/Apache-2.0" +repository = "https://github.com/timothee-haudebourg/khronos-egl" + +[[example]] +name = "wayland-static" +required-features = ["static"] + +[[example]] +name = "wayland-dynamic" +required-features = ["dynamic"] + +[[example]] +name = "load-minimal" +required-features = ["dynamic", "1_4"] +[dependencies.libc] +version = "^0.2" + +[dependencies.libloading] +version = "^0.7" +optional = true +[dev-dependencies.gl] +version = "^0.14" + +[dev-dependencies.wayland-client] +version = "^0.28" + +[dev-dependencies.wayland-egl] +version = "^0.28" + +[dev-dependencies.wayland-protocols] +version = "^0.28" +features = ["client"] +[build-dependencies.pkg-config] +version = "^0.3" +optional = true + +[features] +1_0 = [] +1_1 = ["1_0"] +1_2 = ["1_1"] +1_3 = ["1_2"] +1_4 = ["1_3"] +1_5 = ["1_4"] +default = ["1_5"] +dynamic = ["libloading"] +no-pkg-config = [] +static = ["pkg-config"] diff --git a/third_party/rust/khronos-egl/LICENSE-APACHE b/third_party/rust/khronos-egl/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/khronos-egl/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/khronos-egl/LICENSE-MIT b/third_party/rust/khronos-egl/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/third_party/rust/khronos-egl/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/khronos-egl/README.md b/third_party/rust/khronos-egl/README.md new file mode 100644 index 0000000000..5abc2bb8e4 --- /dev/null +++ b/third_party/rust/khronos-egl/README.md @@ -0,0 +1,165 @@ +# Rust bindings for EGL + +<table><tr> + <td><a href="https://docs.rs/khronos-egl">Documentation</a></td> + <td><a href="https://crates.io/crates/khronos-egl">Crate informations</a></td> + <td><a href="https://github.com/timothee-haudebourg/khronos-egl">Repository</a></td> +</tr></table> + +This crate provides a binding for the Khronos EGL 1.5 API. +It was originally a fork of the [egl](https://crates.io/crates/egl) crate, +which is left unmaintained. + +## Usage + +You can access the EGL API using an [`Instance`](https://docs.rs/khronos-egl/latest/khronos-egl/struct.Instance.html) +object defined by either statically linking with `libEGL.so.1` at compile time, +or dynamically loading the EGL library at runtime. + +### Static linking + +You must enable static linking using the `static` feature in your `Cargo.toml`: +```toml +khronos-egl = { version = ..., features = ["static"] } +``` + +This will add a dependency to the [`pkg-config`](https://crates.io/crates/pkg-config) crate, +necessary to find the EGL library at compile time. + +If you wish to disable linking EGL in this crate, and provide linking in +your crate instead, enable the `no-pkg-config` feature. +```toml +khronos-egl = {version = ..., features = ["static", "no-pkg-config"]} +``` + +Here is a simple example showing how to use this library to create an EGL context when static linking is enabled. + +```rust +extern crate khronos_egl as egl; + +fn main() -> Result<(), egl::Error> { + // Create an EGL API instance. + // The `egl::Static` API implementation is only available when the `static` feature is enabled. + let egl = egl::Instance::new(egl::Static); + + let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + egl.initialize(display)?; + + let attributes = [ + egl::RED_SIZE, 8, + egl::GREEN_SIZE, 8, + egl::BLUE_SIZE, 8, + egl::NONE + ]; + + let config = egl.choose_first_config(display, &attributes)?.expect("unable to find an appropriate ELG configuration"); + + let context_attributes = [ + egl::CONTEXT_MAJOR_VERSION, 4, + egl::CONTEXT_MINOR_VERSION, 0, + egl::CONTEXT_OPENGL_PROFILE_MASK, egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, + egl::NONE + ]; + + egl.create_context(display, config, None, &context_attributes); + + Ok(()) +} +``` + +The creation of a `Display` instance is not detailed here since it depends on your display server. +It is created using the `get_display` function with a pointer to the display server connection handle. +For instance, if you are using the [wayland-client](https://crates.io/crates/wayland-client) crate, +you can get this pointer using the `Display::get_display_ptr` method. + +#### Static API Instance + +It may be bothering in some applications to pass the `Instance` to every fonction that needs to call the EGL API. +One workaround would be to define a static `Instance`, +which should be possible to define at compile time using static linking. +However this is not yet supported by the stable `rustc` compiler. +With the nightly compiler, +you can combine the `nightly` and `static` features so that this crate +can provide a static `Instance`, called `API` that can then be accessed everywhere. + +```rust +use egl::API as egl; +``` + +### Dynamic Linking + +Dynamic linking allows your application to accept multiple versions of EGL and be more flexible. +You must enable dynamic linking using the `dynamic` feature in your `Cargo.toml`: +```toml +khronos-egl = { version = ..., features = ["dynamic"] } +``` + +This will add a dependency to the [`libloading`](https://crates.io/crates/libloading) crate, +necessary to find the EGL library at runtime. +You can then load the EGL API into a `Instance<Dynamic<libloading::Library>>` as follows: + +```rust +let lib = libloading::Library::new("libEGL.so.1").expect("unable to find libEGL.so.1"); +let egl = unsafe { egl::DynamicInstance::<egl::EGL1_4>::load_required_from(lib).expect("unable to load libEGL.so.1") }; +``` + +Here, `egl::EGL1_4` is used to specify what is the minimum required version of EGL that must be provided by `libEGL.so.1`. +This will return a `DynamicInstance<egl::EGL1_4>`, however in that case where `libEGL.so.1` provides a more recent version of EGL, +you can still upcast ths instance to provide version specific features: +```rust +match egl.upcast::<egl::EGL1_5>() { + Some(egl1_5) => { + // do something with EGL 1.5 + } + None => { + // do something with EGL 1.4 instead. + } +}; +``` + +### NixOS + +A `shell.nix` file is present for nix users to build the crate easily. +Just enter a new nix shell using the given configuration file, +and `cargo build` should work. +If you want to run the tests and examples you will need to use `shell-wayland.nix` instead +that will also load wayland since most of them depend on it. + +## Testing + +Most test and examples most be compiled with the `static` feature. + +## Troubleshooting + +### Static Linking with OpenGL ES + +When using OpenGL ES with `khronos-egl` with the `static` feature, +it is necessary to place a dummy extern at the top of your application which links libEGL first, then GLESv1/2. +This is because libEGL provides symbols required by GLESv1/2. +Here's how to work around this: + +```rust +#[link(name = "EGL")] +#[link(name = "GLESv2")] +extern {} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +If the original `egl` crate was licensed only under the Apache 2.0 license, +I believe I have made enough breaking changes so that no relevant code from the +original code remains and the rest can be relicensed. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/third_party/rust/khronos-egl/build.rs b/third_party/rust/khronos-egl/build.rs new file mode 100644 index 0000000000..ae77a040d9 --- /dev/null +++ b/third_party/rust/khronos-egl/build.rs @@ -0,0 +1,12 @@ +#[cfg(all(feature="static", not(feature="no-pkg-config")))] +extern crate pkg_config; + +fn main() { + #[cfg(all(feature="static", not(feature="no-pkg-config")))] + { + pkg_config::Config::new() + .atleast_version("1") + .probe("egl") + .unwrap(); + } +} diff --git a/third_party/rust/khronos-egl/examples/load-minimal.rs b/third_party/rust/khronos-egl/examples/load-minimal.rs new file mode 100644 index 0000000000..409376d678 --- /dev/null +++ b/third_party/rust/khronos-egl/examples/load-minimal.rs @@ -0,0 +1,43 @@ +extern crate gl; +extern crate khronos_egl as egl; +extern crate libloading; + +use std::sync::Arc; +use egl::{ + EGL1_2, + EGL1_4 +}; + +fn main() { + let minimal_egl = unsafe { Arc::new(egl::DynamicInstance::load().expect("unable to load libEGL.so.1")) }; + + println!("EGL version is {}", minimal_egl.version()); + + // Select the rendering API. + if let Some(egl1_2) = minimal_egl.upcast::<EGL1_2>() { + println!("selecting API"); + egl1_2.bind_api(egl::OPENGL_API).expect("unable to select OpenGL API"); + } + + // Setup Open GL. + gl::load_with(|name| minimal_egl.get_proc_address(name).unwrap() as *const std::ffi::c_void); + + match minimal_egl.upcast::<EGL1_4>() { + Some(egl1_4) => { + foo_with_1_4(egl1_4) + }, + None => { + foo_without_1_4(&minimal_egl) + } + } +} + +fn foo_with_1_4<V: egl::api::EGL1_4>(_egl: &egl::Instance<V>) { + println!("with 1.4"); + // do something that requires at least EGL 1.4. +} + +fn foo_without_1_4<V>(_egl: &egl::Instance<V>) { + println!("without 1.4"); + // do something without any specific EGL version (other that 1.0). +}
\ No newline at end of file diff --git a/third_party/rust/khronos-egl/examples/wayland-dynamic.rs b/third_party/rust/khronos-egl/examples/wayland-dynamic.rs new file mode 100644 index 0000000000..cb9748cb68 --- /dev/null +++ b/third_party/rust/khronos-egl/examples/wayland-dynamic.rs @@ -0,0 +1,348 @@ +extern crate gl; +extern crate khronos_egl as egl; +extern crate wayland_client; +extern crate wayland_egl; +extern crate wayland_protocols; +extern crate libloading; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::ptr; +use std::ffi::CStr; +use gl::types::{GLint, GLuint, GLchar, GLenum, GLboolean, GLvoid}; + +use wayland_client::{ + protocol::{wl_compositor::WlCompositor, wl_surface::WlSurface}, + DispatchData, Display, EventQueue, Main +}; + +use wayland_protocols::xdg_shell::client::{ + xdg_surface::{self, XdgSurface}, + xdg_wm_base::{self, XdgWmBase}, +}; + +fn process_xdg_event(xdg: Main<XdgWmBase>, event: xdg_wm_base::Event, _dd: DispatchData) { + use xdg_wm_base::Event::*; + + match event { + Ping { serial } => xdg.pong(serial), + _ => (), + } +} + +struct DisplayConnection { + display: Display, + event_queue: EventQueue, + compositor: Main<WlCompositor>, + xdg: Main<XdgWmBase>, +} + +fn setup_wayland() -> DisplayConnection { + let display = + wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + let mut event_queue = display.create_event_queue(); + let attached_display = display.clone().attach(event_queue.token()); + + let globals = wayland_client::GlobalManager::new(&attached_display); + + // Roundtrip to retrieve the globals list + event_queue + .sync_roundtrip(&mut (), |_, _, _| unreachable!()) + .unwrap(); + + // Get the compositor. + let compositor: Main<WlCompositor> = globals.instantiate_exact(1).unwrap(); + + // Xdg protocol. + let xdg: Main<XdgWmBase> = globals.instantiate_exact(1).unwrap(); + xdg.quick_assign(process_xdg_event); + + DisplayConnection { + display, + event_queue, + compositor, + xdg, + } +} + +fn setup_egl(egl: &egl::DynamicInstance, display: &Display) -> egl::Display { + let egl_display = egl.get_display(display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + egl.initialize(egl_display).unwrap(); + + egl_display +} + +fn create_context(egl: &egl::DynamicInstance, display: egl::Display) -> (egl::Context, egl::Config) { + let attributes = [ + egl::RED_SIZE, + 8, + egl::GREEN_SIZE, + 8, + egl::BLUE_SIZE, + 8, + egl::NONE, + ]; + + let config = egl.choose_first_config(display, &attributes) + .expect("unable to choose an EGL configuration") + .expect("no EGL configuration found"); + + let context_attributes = [ + egl::CONTEXT_MAJOR_VERSION, + 4, + egl::CONTEXT_MINOR_VERSION, + 0, + egl::CONTEXT_OPENGL_PROFILE_MASK, + egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, + egl::NONE, + ]; + + let context = egl.create_context(display, config, None, &context_attributes) + .expect("unable to create an EGL context"); + + (context, config) +} + +struct Surface { + handle: Main<WlSurface>, + initialized: AtomicBool +} + +fn create_surface( + egl: &Arc<egl::DynamicInstance>, + ctx: &DisplayConnection, + egl_display: egl::Display, + egl_context: egl::Context, + egl_config: egl::Config, + width: i32, + height: i32, +) -> Arc<Surface> { + let wl_surface = ctx.compositor.create_surface(); + let xdg_surface = ctx.xdg.get_xdg_surface(&wl_surface); + + let xdg_toplevel = xdg_surface.get_toplevel(); + xdg_toplevel.set_app_id("khronos-egl-test".to_string()); + xdg_toplevel.set_title("Test".to_string()); + + wl_surface.commit(); + ctx.display.flush().unwrap(); + + let surface = Arc::new(Surface { + handle: wl_surface, + initialized: AtomicBool::new(false) + }); + + let weak_surface = Arc::downgrade(&surface); + + let egl = egl.clone(); + xdg_surface.quick_assign(move |xdg_surface: Main<XdgSurface>, event: xdg_surface::Event, _dd: DispatchData| { + use xdg_surface::Event::*; + + match event { + Configure { serial } => { + if let Some(surface) = weak_surface.upgrade() { + if !surface.initialized.swap(true, Ordering::Relaxed) { + let wl_egl_surface = wayland_egl::WlEglSurface::new(&surface.handle, width, height); + + let egl_surface = unsafe { + egl.create_window_surface( + egl_display, + egl_config, + wl_egl_surface.ptr() as egl::NativeWindowType, + None, + ) + .expect("unable to create an EGL surface") + }; + + egl.make_current(egl_display, Some(egl_surface), Some(egl_surface), Some(egl_context)) + .expect("unable to bind the context"); + + render(); + + egl.swap_buffers(egl_display, egl_surface) + .expect("unable to post the surface content"); + + xdg_surface.ack_configure(serial); + } + } + }, + _ => (), + } + }); + + surface +} + +fn main() { + let egl = unsafe { Arc::new(egl::DynamicInstance::<egl::EGL1_5>::load_required().expect("unable to load libEGL.so.1")) }; + + // Setup Open GL. + egl.bind_api(egl::OPENGL_API) + .expect("unable to select OpenGL API"); + gl::load_with(|name| egl.get_proc_address(name).unwrap() as *const std::ffi::c_void); + + // Setup the Wayland client. + let mut ctx = setup_wayland(); + + // Setup EGL. + let egl_display = setup_egl(&egl, &ctx.display); + let (egl_context, egl_config) = create_context(&egl, egl_display); + + // Create a surface. + // Note that it must be kept alive to the end of execution. + let _surface = create_surface(&egl, &ctx, egl_display, egl_context, egl_config, 800, 600); + + loop { + ctx.event_queue + .dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }) + .unwrap(); + } +} + +const VERTEX: &'static [GLint; 8] = &[ + -1, -1, + 1, -1, + 1, 1, + -1, 1 +]; + +const INDEXES: &'static [GLuint; 4] = &[ + 0, 1, 2, 3 +]; + +const VERTEX_SHADER: &[u8] = b"#version 400 +in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0f, 1.0f); +} +\0"; + +const FRAGMENT_SHADER: &[u8] = b"#version 400 +out vec4 color; + +void main() { + color = vec4(1.0f, 0.0f, 0.0f, 1.0f); +} +\0"; + +fn render() { + unsafe { + let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(VERTEX_SHADER).as_ptr(); + gl::ShaderSource(vertex_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(vertex_shader); + check_shader_status(vertex_shader); + + let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(FRAGMENT_SHADER).as_ptr(); + gl::ShaderSource(fragment_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(fragment_shader); + check_shader_status(fragment_shader); + + let program = gl::CreateProgram(); + check_gl_errors(); + gl::AttachShader(program, vertex_shader); + check_gl_errors(); + gl::AttachShader(program, fragment_shader); + check_gl_errors(); + gl::LinkProgram(program); + check_gl_errors(); + gl::UseProgram(program); + check_gl_errors(); + + let mut buffer = 0; + gl::GenBuffers(1, &mut buffer); + check_gl_errors(); + gl::BindBuffer(gl::ARRAY_BUFFER, buffer); + check_gl_errors(); + gl::BufferData( + gl::ARRAY_BUFFER, + 8 * 4, + VERTEX.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + let mut vertex_input = 0; + gl::GenVertexArrays(1, &mut vertex_input); + check_gl_errors(); + gl::BindVertexArray(vertex_input); + check_gl_errors(); + gl::EnableVertexAttribArray(0); + check_gl_errors(); + gl::VertexAttribPointer( + 0, 2, gl::INT, gl::FALSE as GLboolean, 0, 0 as *const GLvoid + ); + check_gl_errors(); + + let mut indexes = 0; + gl::GenBuffers(1, &mut indexes); + check_gl_errors(); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexes); + check_gl_errors(); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + 4 * 4, + INDEXES.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + gl::DrawElements(gl::TRIANGLE_FAN, 4, gl::UNSIGNED_INT, std::ptr::null()); + check_gl_errors(); + } +} + +fn format_error(e: GLenum) -> &'static str { + match e { + gl::NO_ERROR => "No error", + gl::INVALID_ENUM => "Invalid enum", + gl::INVALID_VALUE => "Invalid value", + gl::INVALID_OPERATION => "Invalid operation", + gl::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation", + gl::OUT_OF_MEMORY => "Out of memory", + gl::STACK_UNDERFLOW => "Stack underflow", + gl::STACK_OVERFLOW => "Stack overflow", + _ => "Unknown error" + } +} + +pub fn check_gl_errors() { + unsafe { + match gl::GetError() { + gl::NO_ERROR => (), + e => { + panic!("OpenGL error: {}", format_error(e)) + } + } + } +} + +unsafe fn check_shader_status(shader: GLuint) { + let mut status = gl::FALSE as GLint; + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); + if status != (gl::TRUE as GLint) { + let mut len = 0; + gl::GetProgramiv(shader, gl::INFO_LOG_LENGTH, &mut len); + if len > 0 { + let mut buf = Vec::with_capacity(len as usize); + buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character + gl::GetProgramInfoLog( + shader, + len, + ptr::null_mut(), + buf.as_mut_ptr() as *mut GLchar, + ); + + let log = String::from_utf8(buf).unwrap(); + eprintln!("shader compilation log:\n{}", log); + } + + panic!("shader compilation failed"); + } +} diff --git a/third_party/rust/khronos-egl/examples/wayland-static.rs b/third_party/rust/khronos-egl/examples/wayland-static.rs new file mode 100644 index 0000000000..22522eb9c1 --- /dev/null +++ b/third_party/rust/khronos-egl/examples/wayland-static.rs @@ -0,0 +1,344 @@ +extern crate gl; +extern crate khronos_egl as egl; +extern crate wayland_client; +extern crate wayland_egl; +extern crate wayland_protocols; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::ptr; +use std::ffi::CStr; +use gl::types::{GLint, GLuint, GLchar, GLenum, GLboolean, GLvoid}; +use egl::API as egl; + +use wayland_client::{ + protocol::{wl_compositor::WlCompositor, wl_surface::WlSurface}, + DispatchData, Display, EventQueue, Main +}; + +use wayland_protocols::xdg_shell::client::{ + xdg_surface::{self, XdgSurface}, + xdg_wm_base::{self, XdgWmBase}, +}; + +fn process_xdg_event(xdg: Main<XdgWmBase>, event: xdg_wm_base::Event, _dd: DispatchData) { + use xdg_wm_base::Event::*; + + match event { + Ping { serial } => xdg.pong(serial), + _ => (), + } +} + +struct DisplayConnection { + display: Display, + event_queue: EventQueue, + compositor: Main<WlCompositor>, + xdg: Main<XdgWmBase>, +} + +fn setup_wayland() -> DisplayConnection { + let display = + wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + let mut event_queue = display.create_event_queue(); + let attached_display = display.clone().attach(event_queue.token()); + + let globals = wayland_client::GlobalManager::new(&attached_display); + + // Roundtrip to retrieve the globals list + event_queue + .sync_roundtrip(&mut (), |_, _, _| unreachable!()) + .unwrap(); + + // Get the compositor. + let compositor: Main<WlCompositor> = globals.instantiate_exact(1).unwrap(); + + // Xdg protocol. + let xdg: Main<XdgWmBase> = globals.instantiate_exact(1).unwrap(); + xdg.quick_assign(process_xdg_event); + + DisplayConnection { + display, + event_queue, + compositor, + xdg, + } +} + +fn setup_egl(display: &Display) -> egl::Display { + let egl_display = egl.get_display(display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + egl.initialize(egl_display).unwrap(); + + egl_display +} + +fn create_context(display: egl::Display) -> (egl::Context, egl::Config) { + let attributes = [ + egl::RED_SIZE, + 8, + egl::GREEN_SIZE, + 8, + egl::BLUE_SIZE, + 8, + egl::NONE, + ]; + + let config = egl.choose_first_config(display, &attributes) + .expect("unable to choose an EGL configuration") + .expect("no EGL configuration found"); + + let context_attributes = [ + egl::CONTEXT_MAJOR_VERSION, + 4, + egl::CONTEXT_MINOR_VERSION, + 0, + egl::CONTEXT_OPENGL_PROFILE_MASK, + egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, + egl::NONE, + ]; + + let context = egl.create_context(display, config, None, &context_attributes) + .expect("unable to create an EGL context"); + + (context, config) +} + +struct Surface { + handle: Main<WlSurface>, + initialized: AtomicBool +} + +fn create_surface( + ctx: &DisplayConnection, + egl_display: egl::Display, + egl_context: egl::Context, + egl_config: egl::Config, + width: i32, + height: i32, +) -> Arc<Surface> { + let wl_surface = ctx.compositor.create_surface(); + let xdg_surface = ctx.xdg.get_xdg_surface(&wl_surface); + + let xdg_toplevel = xdg_surface.get_toplevel(); + xdg_toplevel.set_app_id("khronos-egl-test".to_string()); + xdg_toplevel.set_title("Test".to_string()); + + wl_surface.commit(); + ctx.display.flush().unwrap(); + + let surface = Arc::new(Surface { + handle: wl_surface, + initialized: AtomicBool::new(false) + }); + + let weak_surface = Arc::downgrade(&surface); + + xdg_surface.quick_assign(move |xdg_surface: Main<XdgSurface>, event: xdg_surface::Event, _dd: DispatchData| { + use xdg_surface::Event::*; + + match event { + Configure { serial } => { + if let Some(surface) = weak_surface.upgrade() { + if !surface.initialized.swap(true, Ordering::Relaxed) { + let wl_egl_surface = wayland_egl::WlEglSurface::new(&surface.handle, width, height); + + let egl_surface = unsafe { + egl.create_window_surface( + egl_display, + egl_config, + wl_egl_surface.ptr() as egl::NativeWindowType, + None, + ) + .expect("unable to create an EGL surface") + }; + + egl.make_current(egl_display, Some(egl_surface), Some(egl_surface), Some(egl_context)) + .expect("unable to bind the context"); + + render(); + + egl.swap_buffers(egl_display, egl_surface) + .expect("unable to post the surface content"); + + xdg_surface.ack_configure(serial); + } + } + }, + _ => (), + } + }); + + surface +} + +fn main() { + // Setup Open GL. + egl.bind_api(egl::OPENGL_API) + .expect("unable to select OpenGL API"); + gl::load_with(|name| egl.get_proc_address(name).unwrap() as *const std::ffi::c_void); + + // Setup the Wayland client. + let mut ctx = setup_wayland(); + + // Setup EGL. + let egl_display = setup_egl(&ctx.display); + let (egl_context, egl_config) = create_context(egl_display); + + // Create a surface. + // Note that it must be kept alive to the end of execution. + let _surface = create_surface(&ctx, egl_display, egl_context, egl_config, 800, 600); + + loop { + ctx.event_queue + .dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }) + .unwrap(); + } +} + +const VERTEX: &'static [GLint; 8] = &[ + -1, -1, + 1, -1, + 1, 1, + -1, 1 +]; + +const INDEXES: &'static [GLuint; 4] = &[ + 0, 1, 2, 3 +]; + +const VERTEX_SHADER: &[u8] = b"#version 400 +in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0f, 1.0f); +} +\0"; + +const FRAGMENT_SHADER: &[u8] = b"#version 400 +out vec4 color; + +void main() { + color = vec4(1.0f, 0.0f, 0.0f, 1.0f); +} +\0"; + +fn render() { + unsafe { + let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(VERTEX_SHADER).as_ptr(); + gl::ShaderSource(vertex_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(vertex_shader); + check_shader_status(vertex_shader); + + let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(FRAGMENT_SHADER).as_ptr(); + gl::ShaderSource(fragment_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(fragment_shader); + check_shader_status(fragment_shader); + + let program = gl::CreateProgram(); + check_gl_errors(); + gl::AttachShader(program, vertex_shader); + check_gl_errors(); + gl::AttachShader(program, fragment_shader); + check_gl_errors(); + gl::LinkProgram(program); + check_gl_errors(); + gl::UseProgram(program); + check_gl_errors(); + + let mut buffer = 0; + gl::GenBuffers(1, &mut buffer); + check_gl_errors(); + gl::BindBuffer(gl::ARRAY_BUFFER, buffer); + check_gl_errors(); + gl::BufferData( + gl::ARRAY_BUFFER, + 8 * 4, + VERTEX.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + let mut vertex_input = 0; + gl::GenVertexArrays(1, &mut vertex_input); + check_gl_errors(); + gl::BindVertexArray(vertex_input); + check_gl_errors(); + gl::EnableVertexAttribArray(0); + check_gl_errors(); + gl::VertexAttribPointer( + 0, 2, gl::INT, gl::FALSE as GLboolean, 0, 0 as *const GLvoid + ); + check_gl_errors(); + + let mut indexes = 0; + gl::GenBuffers(1, &mut indexes); + check_gl_errors(); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexes); + check_gl_errors(); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + 4 * 4, + INDEXES.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + gl::DrawElements(gl::TRIANGLE_FAN, 4, gl::UNSIGNED_INT, std::ptr::null()); + check_gl_errors(); + } +} + +fn format_error(e: GLenum) -> &'static str { + match e { + gl::NO_ERROR => "No error", + gl::INVALID_ENUM => "Invalid enum", + gl::INVALID_VALUE => "Invalid value", + gl::INVALID_OPERATION => "Invalid operation", + gl::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation", + gl::OUT_OF_MEMORY => "Out of memory", + gl::STACK_UNDERFLOW => "Stack underflow", + gl::STACK_OVERFLOW => "Stack overflow", + _ => "Unknown error" + } +} + +pub fn check_gl_errors() { + unsafe { + match gl::GetError() { + gl::NO_ERROR => (), + e => { + panic!("OpenGL error: {}", format_error(e)) + } + } + } +} + +unsafe fn check_shader_status(shader: GLuint) { + let mut status = gl::FALSE as GLint; + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); + if status != (gl::TRUE as GLint) { + let mut len = 0; + gl::GetProgramiv(shader, gl::INFO_LOG_LENGTH, &mut len); + if len > 0 { + let mut buf = Vec::with_capacity(len as usize); + buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character + gl::GetProgramInfoLog( + shader, + len, + ptr::null_mut(), + buf.as_mut_ptr() as *mut GLchar, + ); + + let log = String::from_utf8(buf).unwrap(); + eprintln!("shader compilation log:\n{}", log); + } + + panic!("shader compilation failed"); + } +} diff --git a/third_party/rust/khronos-egl/shell-wayland.nix b/third_party/rust/khronos-egl/shell-wayland.nix new file mode 100644 index 0000000000..f681f91231 --- /dev/null +++ b/third_party/rust/khronos-egl/shell-wayland.nix @@ -0,0 +1,11 @@ +{ pkgs ? import <nixpkgs> {} }: + +pkgs.mkShell { + buildInputs = [ + pkgs.gcc + pkgs.libGL + pkgs.pkg-config + pkgs.wayland + ]; + LD_LIBRARY_PATH="${pkgs.libGL}/lib"; +} diff --git a/third_party/rust/khronos-egl/shell.nix b/third_party/rust/khronos-egl/shell.nix new file mode 100644 index 0000000000..bf6c1364df --- /dev/null +++ b/third_party/rust/khronos-egl/shell.nix @@ -0,0 +1,10 @@ +{ pkgs ? import <nixpkgs> {} }: + +pkgs.mkShell { + buildInputs = [ + pkgs.gcc + pkgs.libGL + pkgs.pkg-config + ]; + LD_LIBRARY_PATH="${pkgs.libGL}/lib"; +}
\ No newline at end of file diff --git a/third_party/rust/khronos-egl/src/lib.rs b/third_party/rust/khronos-egl/src/lib.rs new file mode 100644 index 0000000000..239ff19194 --- /dev/null +++ b/third_party/rust/khronos-egl/src/lib.rs @@ -0,0 +1,2363 @@ +//! This crate provides a binding for the Khronos EGL 1.5 API. +//! It was originally a fork of the [egl](https://crates.io/crates/egl) crate, +//! which is left unmaintained. +//! +//! ## Usage +//! +//! You can access the EGL API using an [`Instance`] +//! object defined by either statically linking with `libEGL.so.1` at compile time, +//! or dynamically loading the EGL library at runtime. +//! +//! ### Static linking +//! +//! You must enable static linking using the `static` feature in your `Cargo.toml`: +//! ```toml +//! khronos-egl = { version = ..., features = ["static"] } +//! ``` +//! +//! This will add a dependency to the [`pkg-config`](https://crates.io/crates/pkg-config) crate, +//! necessary to find the EGL library at compile time. +//! Here is a simple example showing how to use this library to create an EGL context when static linking is enabled. +//! +//! ```rust +//! extern crate khronos_egl as egl; +//! +//! fn main() -> Result<(), egl::Error> { +//! // Create an EGL API instance. +//! // The `egl::Static` API implementation is only available when the `static` feature is enabled. +//! let egl = egl::Instance::new(egl::Static); +//! +//! let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); +//! let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); +//! egl.initialize(display)?; +//! +//! let attributes = [ +//! egl::RED_SIZE, 8, +//! egl::GREEN_SIZE, 8, +//! egl::BLUE_SIZE, 8, +//! egl::NONE +//! ]; +//! +//! let config = egl.choose_first_config(display, &attributes)?.expect("unable to find an appropriate ELG configuration"); +//! +//! let context_attributes = [ +//! egl::CONTEXT_MAJOR_VERSION, 4, +//! egl::CONTEXT_MINOR_VERSION, 0, +//! egl::CONTEXT_OPENGL_PROFILE_MASK, egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, +//! egl::NONE +//! ]; +//! +//! egl.create_context(display, config, None, &context_attributes); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The creation of a `Display` instance is not detailed here since it depends on your display server. +//! It is created using the `get_display` function with a pointer to the display server connection handle. +//! For instance, if you are using the [wayland-client](https://crates.io/crates/wayland-client) crate, +//! you can get this pointer using the `Display::get_display_ptr` method. +//! +//! #### Static API Instance +//! +//! It may be bothering in some applications to pass the `Instance` to every fonction that needs to call the EGL API. +//! One workaround would be to define a static `Instance`, +//! which should be possible to define at compile time using static linking. +//! However this is not yet supported by the stable `rustc` compiler. +//! With the nightly compiler, +//! you can combine the `nightly` and `static` features so that this crate +//! can provide a static `Instance`, called `API` that can then be accessed everywhere. +//! +//! ``` +//! # extern crate khronos_egl as egl; +//! use egl::API as egl; +//! ``` +//! +//! ### Dynamic Linking +//! +//! Dynamic linking allows your application to accept multiple versions of EGL and be more flexible. +//! You must enable dynamic linking using the `dynamic` feature in your `Cargo.toml`: +//! ```toml +//! khronos-egl = { version = ..., features = ["dynamic"] } +//! ``` +//! +//! This will add a dependency to the [`libloading`](https://crates.io/crates/libloading) crate, +//! necessary to find the EGL library at runtime. +//! You can then load the EGL API into a `Instance<Dynamic<libloading::Library>>` as follows: +//! +//! ``` +//! # extern crate khronos_egl as egl; +//! let lib = libloading::Library::new("libEGL.so.1").expect("unable to find libEGL.so.1"); +//! let egl = unsafe { egl::DynamicInstance::<egl::EGL1_4>::load_required_from(lib).expect("unable to load libEGL.so.1") }; +//! ``` +//! +//! Here, `egl::EGL1_4` is used to specify what is the minimum required version of EGL that must be provided by `libEGL.so.1`. +//! This will return a `DynamicInstance<egl::EGL1_4>`, however in that case where `libEGL.so.1` provides a more recent version of EGL, +//! you can still upcast ths instance to provide version specific features: +//! ``` +//! # extern crate khronos_egl as egl; +//! # let lib = libloading::Library::new("libEGL.so.1").expect("unable to find libEGL.so.1"); +//! # let egl = unsafe { egl::DynamicInstance::<egl::EGL1_4>::load_required_from(lib).expect("unable to load libEGL.so.1") }; +//! match egl.upcast::<egl::EGL1_5>() { +//! Some(egl1_5) => { +//! // do something with EGL 1.5 +//! } +//! None => { +//! // do something with EGL 1.4 instead. +//! } +//! }; +//! ``` +//! +//! ## Troubleshooting +//! +//! ### Static Linking with OpenGL ES +//! +//! When using OpenGL ES with `khronos-egl` with the `static` feature, +//! it is necessary to place a dummy extern at the top of your application which links libEGL first, then GLESv1/2. +//! This is because libEGL provides symbols required by GLESv1/2. +//! Here's how to work around this: +//! +//! ``` +//! ##[link(name = "EGL")] +//! ##[link(name = "GLESv2")] +//! extern {} +//! ``` +#![allow(non_upper_case_globals)] +#![allow(non_snake_case)] + +extern crate libc; + +use std::convert::{TryFrom, TryInto}; +use std::ffi::CStr; +use std::ffi::CString; +use std::fmt; +use std::ptr; + +use libc::{c_uint, c_char, c_void}; + +/// EGL API provider. +pub trait Api { + /// Version of the provided EGL API. + fn version(&self) -> Version; +} + +pub trait Downcast<V> { + fn downcast(&self) -> &V; +} + +impl<T> Downcast<T> for T { + fn downcast(&self) -> &T { + self + } +} + +pub trait Upcast<V> { + fn upcast(&self) -> Option<&V>; +} + +impl<T> Upcast<T> for T { + fn upcast(&self) -> Option<&T> { + Some(self) + } +} + +/// EGL API instance. +/// +/// An instance wraps an interface to the EGL API and provide +/// rust-friendly access to it. +pub struct Instance<T> { + api: T +} + +impl<T> Instance<T> { + /// Cast the API. + #[inline(always)] + pub fn cast_into<U: From<T>>(self) -> Instance<U> { + Instance { + api: self.api.into() + } + } + + /// Try to cast the API. + #[inline(always)] + pub fn try_cast_into<U: TryFrom<T>>(self) -> Result<Instance<U>, Instance<U::Error>> { + match self.api.try_into() { + Ok(t) => Ok(Instance { + api: t + }), + Err(e) => Err(Instance { + api: e + }) + } + } + + /// Returns the version of the provided EGL API. + #[inline(always)] + pub fn version(&self) -> Version where T: Api { + self.api.version() + } +} + +impl<T> Instance<T> { + #[inline(always)] + pub const fn new(api: T) -> Instance<T> { + Instance { + api + } + } +} + +impl<T: fmt::Debug> fmt::Debug for Instance<T> { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Instance({:?})", self.api) + } +} + +impl<T> From<T> for Instance<T> { + #[inline(always)] + fn from(t: T) -> Instance<T> { + Instance::new(t) + } +} + +// ------------------------------------------------------------------------------------------------ +// EGL 1.0 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_0")] +mod egl1_0 { + use super::*; + + pub type Boolean = c_uint; + pub type Int = i32; + pub type Attrib = usize; + pub type EGLDisplay = *mut c_void; + pub type EGLConfig = *mut c_void; + pub type EGLContext = *mut c_void; + pub type EGLSurface = *mut c_void; + pub type NativeDisplayType = *mut c_void; + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Display(pub(crate) EGLDisplay); + + impl Display { + #[inline] + pub unsafe fn from_ptr(ptr: EGLDisplay) -> Display { + Display(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLDisplay { + self.0 + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Config(pub(crate) EGLConfig); + + impl Config { + #[inline] + pub unsafe fn from_ptr(ptr: EGLConfig) -> Config { + Config(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLConfig { + self.0 + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Context(pub(crate) EGLContext); + + impl Context { + #[inline] + pub unsafe fn from_ptr(ptr: EGLContext) -> Context { + Context(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLContext { + self.0 + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Surface(pub(crate) EGLSurface); + + impl Surface { + #[inline] + pub unsafe fn from_ptr(ptr: EGLSurface) -> Surface { + Surface(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLSurface { + self.0 + } + } + + #[cfg(not(android))] + pub type NativePixmapType = *mut c_void; + + #[cfg(not(android))] + pub type NativeWindowType = *mut c_void; + + #[repr(C)] + #[cfg(android)] + struct android_native_window_t; + + #[repr(C)] + #[cfg(android)] + struct egl_native_pixmap_t; + + #[cfg(android)] + pub type NativePixmapType = *mut egl_native_pixmap_t; + + #[cfg(android)] + pub type NativeWindowType = *mut android_native_window_t; + + pub const ALPHA_SIZE: Int = 0x3021; + pub const BAD_ACCESS: Int = 0x3002; + pub const BAD_ALLOC: Int = 0x3003; + pub const BAD_ATTRIBUTE: Int = 0x3004; + pub const BAD_CONFIG: Int = 0x3005; + pub const BAD_CONTEXT: Int = 0x3006; + pub const BAD_CURRENT_SURFACE: Int = 0x3007; + pub const BAD_DISPLAY: Int = 0x3008; + pub const BAD_MATCH: Int = 0x3009; + pub const BAD_NATIVE_PIXMAP: Int = 0x300A; + pub const BAD_NATIVE_WINDOW: Int = 0x300B; + pub const BAD_PARAMETER: Int = 0x300C; + pub const BAD_SURFACE: Int = 0x300D; + pub const BLUE_SIZE: Int = 0x3022; + pub const BUFFER_SIZE: Int = 0x3020; + pub const CONFIG_CAVEAT: Int = 0x3027; + pub const CONFIG_ID: Int = 0x3028; + pub const CORE_NATIVE_ENGINE: Int = 0x305B; + pub const DEPTH_SIZE: Int = 0x3025; + pub const DONT_CARE: Int = -1; + pub const DRAW: Int = 0x3059; + pub const EXTENSIONS: Int = 0x3055; + pub const FALSE: Boolean = 0; + pub const GREEN_SIZE: Int = 0x3023; + pub const HEIGHT: Int = 0x3056; + pub const LARGEST_PBUFFER: Int = 0x3058; + pub const LEVEL: Int = 0x3029; + pub const MAX_PBUFFER_HEIGHT: Int = 0x302A; + pub const MAX_PBUFFER_PIXELS: Int = 0x302B; + pub const MAX_PBUFFER_WIDTH: Int = 0x302C; + pub const NATIVE_RENDERABLE: Int = 0x302D; + pub const NATIVE_VISUAL_ID: Int = 0x302E; + pub const NATIVE_VISUAL_TYPE: Int = 0x302F; + pub const NONE: Int = 0x3038; + pub const ATTRIB_NONE: Attrib = 0x3038; + pub const NON_CONFORMANT_CONFIG: Int = 0x3051; + pub const NOT_INITIALIZED: Int = 0x3001; + pub const NO_CONTEXT: EGLContext = 0 as EGLContext; + pub const NO_DISPLAY: EGLDisplay = 0 as EGLDisplay; + pub const NO_SURFACE: EGLSurface = 0 as EGLSurface; + pub const PBUFFER_BIT: Int = 0x0001; + pub const PIXMAP_BIT: Int = 0x0002; + pub const READ: Int = 0x305A; + pub const RED_SIZE: Int = 0x3024; + pub const SAMPLES: Int = 0x3031; + pub const SAMPLE_BUFFERS: Int = 0x3032; + pub const SLOW_CONFIG: Int = 0x3050; + pub const STENCIL_SIZE: Int = 0x3026; + pub const SUCCESS: Int = 0x3000; + pub const SURFACE_TYPE: Int = 0x3033; + pub const TRANSPARENT_BLUE_VALUE: Int = 0x3035; + pub const TRANSPARENT_GREEN_VALUE: Int = 0x3036; + pub const TRANSPARENT_RED_VALUE: Int = 0x3037; + pub const TRANSPARENT_RGB: Int = 0x3052; + pub const TRANSPARENT_TYPE: Int = 0x3034; + pub const TRUE: Boolean = 1; + pub const VENDOR: Int = 0x3053; + pub const VERSION: Int = 0x3054; + pub const WIDTH: Int = 0x3057; + pub const WINDOW_BIT: Int = 0x0004; + + /// EGL errors. + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + pub enum Error { + /// EGL is not initialized, or could not be initialized, for the specified + /// EGL display connection. + NotInitialized, + + /// EGL cannot access a requested resource (for example a context is bound + /// in another thread). + BadAccess, + + /// EGL failed to allocate resources for the requested operation. + BadAlloc, + + /// An unrecognized attribute or attribute value was passed in the attribute + /// list. + BadAttribute, + + /// An Context argument does not name a valid EGL rendering context. + BadContext, + + /// An Config argument does not name a valid EGL frame buffer configuration. + BadConfig, + + /// The current surface of the calling thread is a window, pixel buffer or + /// pixmap that is no longer valid. + BadCurrentSurface, + + /// An Display argument does not name a valid EGL display connection. + BadDisplay, + + /// An Surface argument does not name a valid surface (window, pixel buffer + /// or pixmap) configured for GL rendering. + BadSurface, + + /// Arguments are inconsistent (for example, a valid context requires + /// buffers not supplied by a valid surface). + BadMatch, + + /// One or more argument values are invalid. + BadParameter, + + /// A NativePixmapType argument does not refer to a valid native pixmap. + BadNativePixmap, + + /// A NativeWindowType argument does not refer to a valid native window. + BadNativeWindow, + + /// A power management event has occurred. The application must destroy all + /// contexts and reinitialise OpenGL ES state and objects to continue + /// rendering. + ContextLost, + } + + impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } + } + + impl Error { + pub fn native(&self) -> Int { + use Error::*; + match self { + NotInitialized => NOT_INITIALIZED, + BadAccess => BAD_ACCESS, + BadAlloc => BAD_ALLOC, + BadAttribute => BAD_ATTRIBUTE, + BadContext => BAD_CONTEXT, + BadConfig => BAD_CONFIG, + BadCurrentSurface => BAD_CURRENT_SURFACE, + BadDisplay => BAD_DISPLAY, + BadSurface => BAD_SURFACE, + BadMatch => BAD_MATCH, + BadParameter => BAD_PARAMETER, + BadNativePixmap => BAD_NATIVE_PIXMAP, + BadNativeWindow => BAD_NATIVE_WINDOW, + ContextLost => CONTEXT_LOST, + } + } + + fn message(&self) -> &'static str { + use Error::*; + match self { + NotInitialized => "EGL is not initialized, or could not be initialized, for the specified EGL display connection.", + BadAccess => "EGL cannot access a requested resource (for example a context is bound in another thread.", + BadAlloc => "EGL failed to allocate resources for the requested operation.", + BadAttribute => "An unrecognized attribute or attribute value was passed in the attribute list.", + BadContext => "An Context argument does not name a valid EGL rendering context.", + BadConfig => "An Config argument does not name a valid EGL frame buffer configuration.", + BadCurrentSurface => "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid.", + BadDisplay => "An Display argument does not name a valid EGL display connection.", + BadSurface => "An Surface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering.", + BadMatch => "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface.", + BadParameter => "One or more argument values are invalid.", + BadNativePixmap => "A NativePixmapType argument does not refer to a valid native pixmap.", + BadNativeWindow => "A NativeWindowType argument does not refer to a valid native window.", + ContextLost => "A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering." + } + } + } + + impl From<Error> for Int { + fn from(e: Error) -> Int { + e.native() + } + } + + impl TryFrom<Int> for Error { + type Error = Int; + + fn try_from(e: Int) -> Result<Error, Int> { + use Error::*; + match e { + NOT_INITIALIZED => Ok(NotInitialized), + BAD_ACCESS => Ok(BadAccess), + BAD_ALLOC => Ok(BadAlloc), + BAD_ATTRIBUTE => Ok(BadAttribute), + BAD_CONTEXT => Ok(BadContext), + BAD_CONFIG => Ok(BadConfig), + BAD_CURRENT_SURFACE => Ok(BadCurrentSurface), + BAD_DISPLAY => Ok(BadDisplay), + BAD_SURFACE => Ok(BadSurface), + BAD_MATCH => Ok(BadMatch), + BAD_PARAMETER => Ok(BadParameter), + BAD_NATIVE_PIXMAP => Ok(BadNativePixmap), + BAD_NATIVE_WINDOW => Ok(BadNativeWindow), + CONTEXT_LOST => Ok(ContextLost), + _ => Err(e), + } + } + } + + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.message().fmt(f) + } + } + + pub fn check_int_list(attrib_list: &[Int]) -> Result<(), Error> { + if attrib_list.last() == Some(&NONE) { + Ok(()) + } else { + Err(Error::BadParameter) + } + } + + pub fn check_attrib_list(attrib_list: &[Attrib]) -> Result<(), Error> { + if attrib_list.last() == Some(&ATTRIB_NONE) { + Ok(()) + } else { + Err(Error::BadParameter) + } + } + + impl<T: api::EGL1_0> Instance<T> { + /// Return the number of EGL frame buffer configurations that atch specified + /// attributes. + /// + /// This will call `eglChooseConfig` without `null` as `configs` to get the + /// number of matching configurations. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + pub fn matching_config_count(&self, display: Display, attrib_list: &[Int]) -> Result<usize, Error> { + check_int_list(attrib_list)?; + unsafe { + let mut count = 0; + + if self.api.eglChooseConfig( + display.as_ptr(), + attrib_list.as_ptr(), + ptr::null_mut(), + 0, + &mut count, + ) == TRUE + { + Ok(count as usize) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return a list of EGL frame buffer configurations that match specified + /// attributes. + /// + /// This will write as many matching configurations in `configs` up to its + /// capacity. You can use the function [`matching_config_count`](Self::matching_config_count) to get the + /// exact number of configurations matching the specified attributes. + /// + /// ## Example + /// + /// ``` + /// # extern crate khronos_egl as egl; + /// # extern crate wayland_client; + /// # fn main() -> Result<(), egl::Error> { + /// # let egl = egl::Instance::new(egl::Static); + /// # let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + /// # let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + /// # egl.initialize(display)?; + /// # let attrib_list = [egl::RED_SIZE, 8, egl::GREEN_SIZE, 8, egl::BLUE_SIZE, 8, egl::NONE]; + /// // Get the number of matching configurations. + /// let count = egl.matching_config_count(display, &attrib_list)?; + /// + /// // Get the matching configurations. + /// let mut configs = Vec::with_capacity(count); + /// egl.choose_config(display, &attrib_list, &mut configs)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + pub fn choose_config(&self, display: Display, attrib_list: &[Int], configs: &mut Vec<Config>) -> Result<(), Error> { + check_int_list(attrib_list)?; + unsafe { + let capacity = configs.capacity(); + let mut count = 0; + + if self.api.eglChooseConfig( + display.as_ptr(), + attrib_list.as_ptr(), + configs.as_mut_ptr() as *mut EGLConfig, + capacity.try_into().unwrap(), + &mut count, + ) == TRUE + { + configs.set_len(count as usize); + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return the first EGL frame buffer configuration that match specified + /// attributes. + /// + /// This is an helper function that will call `choose_config` with a buffer of + /// size 1, which is equivalent to: + /// ``` + /// # extern crate khronos_egl as egl; + /// # extern crate wayland_client; + /// # fn main() -> Result<(), egl::Error> { + /// # let egl = egl::Instance::new(egl::Static); + /// # let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + /// # let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + /// # egl.initialize(display)?; + /// # let attrib_list = [egl::RED_SIZE, 8, egl::GREEN_SIZE, 8, egl::BLUE_SIZE, 8, egl::NONE]; + /// let mut configs = Vec::with_capacity(1); + /// egl.choose_config(display, &attrib_list, &mut configs)?; + /// configs.first(); + /// # Ok(()) + /// # } + /// ``` + pub fn choose_first_config(&self, display: Display, attrib_list: &[Int]) -> Result<Option<Config>, Error> { + let mut configs = Vec::with_capacity(1); + self.choose_config(display, attrib_list, &mut configs)?; + Ok(configs.first().map(|config| *config)) + } + + /// Copy EGL surface color buffer to a native pixmap. + pub fn copy_buffers(&self, display: Display, surface: Surface, target: NativePixmapType) -> Result<(), Error> { + unsafe { + if self.api.eglCopyBuffers(display.as_ptr(), surface.as_ptr(), target) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Create a new EGL rendering context. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + pub fn create_context(&self, display: Display, config: Config, share_context: Option<Context>, attrib_list: &[Int]) -> Result<Context, Error> { + check_int_list(attrib_list)?; + unsafe { + let share_context = match share_context { + Some(share_context) => share_context.as_ptr(), + None => NO_CONTEXT, + }; + + let context = self.api.eglCreateContext( + display.as_ptr(), + config.as_ptr(), + share_context, + attrib_list.as_ptr(), + ); + + if context != NO_CONTEXT { + Ok(Context(context)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Create a new EGL pixel buffer surface. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + pub fn create_pbuffer_surface(&self, display: Display, config: Config, attrib_list: &[Int]) -> Result<Surface, Error> { + check_int_list(attrib_list)?; + unsafe { + let surface = + self.api.eglCreatePbufferSurface(display.as_ptr(), config.as_ptr(), attrib_list.as_ptr()); + + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Create a new EGL offscreen surface. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + /// + /// Since this function may raise undefined behavior if the display and native + /// pixmap do not belong to the same platform, it is inherently unsafe. + pub unsafe fn create_pixmap_surface(&self, display: Display, config: Config, pixmap: NativePixmapType, attrib_list: &[Int]) -> Result<Surface, Error> { + check_int_list(attrib_list)?; + let surface = self.api.eglCreatePixmapSurface( + display.as_ptr(), + config.as_ptr(), + pixmap, + attrib_list.as_ptr(), + ); + + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Create a new EGL window surface. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + /// + /// Since this function may raise undefined behavior if the display and native + /// window do not belong to the same platform, it is inherently unsafe. + pub unsafe fn create_window_surface(&self, display: Display, config: Config, window: NativeWindowType, attrib_list: Option<&[Int]>) -> Result<Surface, Error> { + let attrib_list = match attrib_list { + Some(attrib_list) => { + check_int_list(attrib_list)?; + attrib_list.as_ptr() + } + None => ptr::null(), + }; + + let surface = + self.api.eglCreateWindowSurface(display.as_ptr(), config.as_ptr(), window, attrib_list); + + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Destroy an EGL rendering context. + pub fn destroy_context(&self, display: Display, ctx: Context) -> Result<(), Error> { + unsafe { + if self.api.eglDestroyContext(display.as_ptr(), ctx.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Destroy an EGL surface. + pub fn destroy_surface(&self, display: Display, surface: Surface) -> Result<(), Error> { + unsafe { + if self.api.eglDestroySurface(display.as_ptr(), surface.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return information about an EGL frame buffer configuration. + pub fn get_config_attrib(&self, display: Display, config: Config, attribute: Int) -> Result<Int, Error> { + unsafe { + let mut value: Int = 0; + if self.api.eglGetConfigAttrib(display.as_ptr(), config.as_ptr(), attribute, &mut value) == TRUE + { + Ok(value) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return the number of all frame buffer configurations. + /// + /// You can use it to setup the correct capacity for the configurations buffer in [`get_configs`](Self::get_configs). + /// + /// ## Example + /// ``` + /// # extern crate khronos_egl as egl; + /// # extern crate wayland_client; + /// # fn main() -> Result<(), egl::Error> { + /// # let egl = egl::Instance::new(egl::Static); + /// # let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + /// # let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + /// # egl.initialize(display)?; + /// let mut configs = Vec::with_capacity(egl.get_config_count(display)?); + /// egl.get_configs(display, &mut configs); + /// assert!(configs.len() > 0); + /// # Ok(()) + /// # } + /// ``` + pub fn get_config_count(&self, display: Display) -> Result<usize, Error> { + unsafe { + let mut count = 0; + + if self.api.eglGetConfigs(display.as_ptr(), std::ptr::null_mut(), 0, &mut count) == TRUE { + Ok(count as usize) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Get the list of all EGL frame buffer configurations for a display. + /// + /// The configurations are added to the `configs` buffer, up to the buffer's capacity. + /// You can use [`get_config_count`](Self::get_config_count) to get the total number of available frame buffer configurations, + /// and setup the buffer's capacity accordingly. + /// + /// ## Example + /// ``` + /// # extern crate khronos_egl as egl; + /// # extern crate wayland_client; + /// # fn main() -> Result<(), egl::Error> { + /// # let egl = egl::Instance::new(egl::Static); + /// # let wayland_display = wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + /// # let display = egl.get_display(wayland_display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + /// # egl.initialize(display)?; + /// let mut configs = Vec::with_capacity(egl.get_config_count(display)?); + /// egl.get_configs(display, &mut configs); + /// # Ok(()) + /// # } + /// ``` + pub fn get_configs(&self, display: Display, configs: &mut Vec<Config>) -> Result<(), Error> { + unsafe { + let capacity = configs.capacity(); + let mut count = 0; + + if self.api.eglGetConfigs( + display.as_ptr(), + configs.as_mut_ptr() as *mut EGLConfig, + capacity.try_into().unwrap(), + &mut count, + ) == TRUE + { + configs.set_len(count as usize); + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return the display for the current EGL rendering context. + pub fn get_current_display(&self) -> Option<Display> { + unsafe { + let display = self.api.eglGetCurrentDisplay(); + + if display != NO_DISPLAY { + Some(Display(display)) + } else { + None + } + } + } + + /// Return the read or draw surface for the current EGL rendering context. + pub fn get_current_surface(&self, readdraw: Int) -> Option<Surface> { + unsafe { + let surface = self.api.eglGetCurrentSurface(readdraw); + + if surface != NO_SURFACE { + Some(Surface(surface)) + } else { + None + } + } + } + + /// Return an EGL display connection. + pub fn get_display(&self, display_id: NativeDisplayType) -> Option<Display> { + unsafe { + let display = self.api.eglGetDisplay(display_id); + + if display != NO_DISPLAY { + Some(Display(display)) + } else { + None + } + } + } + + /// Return error information. + /// + /// Return the error of the last called EGL function in the current thread, or + /// `None` if the error is set to `SUCCESS`. + /// + /// Note that since a call to `eglGetError` sets the error to `SUCCESS`, and + /// since this function is automatically called by any wrapper function + /// returning a `Result` when necessary, this function may only return `None` + /// from the point of view of a user. + pub fn get_error(&self) -> Option<Error> { + unsafe { + let e = self.api.eglGetError(); + if e == SUCCESS { + None + } else { + Some(e.try_into().unwrap()) + } + } + } + + /// Return a GL or an EGL extension function. + pub fn get_proc_address(&self, procname: &str) -> Option<extern "C" fn()> { + unsafe { + let string = CString::new(procname).unwrap(); + + let addr = self.api.eglGetProcAddress(string.as_ptr()); + if !(addr as *const ()).is_null() { + Some(addr) + } else { + None + } + } + } + + /// Initialize an EGL display connection. + pub fn initialize(&self, display: Display) -> Result<(Int, Int), Error> { + unsafe { + let mut major = 0; + let mut minor = 0; + + if self.api.eglInitialize(display.as_ptr(), &mut major, &mut minor) == TRUE { + Ok((major, minor)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Attach an EGL rendering context to EGL surfaces. + pub fn make_current(&self, display: Display, draw: Option<Surface>, read: Option<Surface>, ctx: Option<Context>) -> Result<(), Error> { + unsafe { + let draw = match draw { + Some(draw) => draw.as_ptr(), + None => NO_SURFACE, + }; + let read = match read { + Some(read) => read.as_ptr(), + None => NO_SURFACE, + }; + let ctx = match ctx { + Some(ctx) => ctx.as_ptr(), + None => NO_CONTEXT, + }; + + if self.api.eglMakeCurrent(display.as_ptr(), draw, read, ctx) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return EGL rendering context information. + pub fn query_context(&self, display: Display, ctx: Context, attribute: Int) -> Result<Int, Error> { + unsafe { + let mut value = 0; + if self.api.eglQueryContext(display.as_ptr(), ctx.as_ptr(), attribute, &mut value) == TRUE { + Ok(value) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return a string describing properties of the EGL client or of an EGL display + /// connection. + pub fn query_string(&self, display: Option<Display>, name: Int) -> Result<&'static CStr, Error> { + unsafe { + let display_ptr = match display { + Some(display) => display.as_ptr(), + None => NO_DISPLAY + }; + + let c_str = self.api.eglQueryString(display_ptr, name); + + if !c_str.is_null() { + Ok(CStr::from_ptr(c_str)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return EGL surface information. + pub fn query_surface(&self, display: Display, surface: Surface, attribute: Int) -> Result<Int, Error> { + unsafe { + let mut value = 0; + if self.api.eglQuerySurface(display.as_ptr(), surface.as_ptr(), attribute, &mut value) == TRUE { + Ok(value) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Post EGL surface color buffer to a native window. + pub fn swap_buffers(&self, display: Display, surface: Surface) -> Result<(), Error> { + unsafe { + if self.api.eglSwapBuffers(display.as_ptr(), surface.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Terminate an EGL display connection. + pub fn terminate(&self, display: Display) -> Result<(), Error> { + unsafe { + if self.api.eglTerminate(display.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Complete GL execution prior to subsequent native rendering calls. + pub fn wait_gl(&self) -> Result<(), Error> { + unsafe { + if self.api.eglWaitGL() == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Complete native execution prior to subsequent GL rendering calls. + pub fn wait_native(&self, engine: Int) -> Result<(), Error> { + unsafe { + if self.api.eglWaitNative(engine) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + } +} + +#[cfg(feature = "1_0")] +pub use egl1_0::*; + +// ------------------------------------------------------------------------------------------------ +// EGL 1.1 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_1")] +mod egl1_1 { + use super::*; + + pub const BACK_BUFFER: Int = 0x3084; + pub const BIND_TO_TEXTURE_RGB: Int = 0x3039; + pub const BIND_TO_TEXTURE_RGBA: Int = 0x303A; + pub const CONTEXT_LOST: Int = 0x300E; + pub const MIN_SWAP_INTERVAL: Int = 0x303B; + pub const MAX_SWAP_INTERVAL: Int = 0x303C; + pub const MIPMAP_TEXTURE: Int = 0x3082; + pub const MIPMAP_LEVEL: Int = 0x3083; + pub const NO_TEXTURE: Int = 0x305C; + pub const TEXTURE_2D: Int = 0x305F; + pub const TEXTURE_FORMAT: Int = 0x3080; + pub const TEXTURE_RGB: Int = 0x305D; + pub const TEXTURE_RGBA: Int = 0x305E; + pub const TEXTURE_TARGET: Int = 0x3081; + + impl<T: api::EGL1_1> Instance<T> { + /// Defines a two-dimensional texture image. + pub fn bind_tex_image(&self, display: Display, surface: Surface, buffer: Int) -> Result<(), Error> { + unsafe { + if self.api.eglBindTexImage(display.as_ptr(), surface.as_ptr(), buffer) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Releases a color buffer that is being used as a texture. + pub fn release_tex_image(&self, display: Display, surface: Surface, buffer: Int) -> Result<(), Error> { + unsafe { + if self.api.eglReleaseTexImage(display.as_ptr(), surface.as_ptr(), buffer) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Set an EGL surface attribute. + pub fn surface_attrib(&self, display: Display, surface: Surface, attribute: Int, value: Int) -> Result<(), Error> { + unsafe { + if self.api.eglSurfaceAttrib(display.as_ptr(), surface.as_ptr(), attribute, value) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Specifies the minimum number of video frame periods per buffer swap for the + /// window associated with the current context. + pub fn swap_interval(&self, display: Display, interval: Int) -> Result<(), Error> { + unsafe { + if self.api.eglSwapInterval(display.as_ptr(), interval) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + } +} + +#[cfg(feature = "1_1")] +pub use egl1_1::*; + +// ------------------------------------------------------------------------------------------------ +// EGL 1.2 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_2")] +mod egl1_2 { + use super::*; + + pub type Enum = c_uint; + pub type EGLClientBuffer = *mut c_void; + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct ClientBuffer(EGLClientBuffer); + + impl ClientBuffer { + #[inline] + pub unsafe fn from_ptr(ptr: EGLClientBuffer) -> ClientBuffer { + ClientBuffer(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLClientBuffer { + self.0 + } + } + + pub const ALPHA_FORMAT: Int = 0x3088; + pub const ALPHA_FORMAT_NONPRE: Int = 0x308B; + pub const ALPHA_FORMAT_PRE: Int = 0x308C; + pub const ALPHA_MASK_SIZE: Int = 0x303E; + pub const BUFFER_PRESERVED: Int = 0x3094; + pub const BUFFER_DESTROYED: Int = 0x3095; + pub const CLIENT_APIS: Int = 0x308D; + pub const COLORSPACE: Int = 0x3087; + pub const COLORSPACE_sRGB: Int = 0x3089; + pub const COLORSPACE_LINEAR: Int = 0x308A; + pub const COLOR_BUFFER_TYPE: Int = 0x303F; + pub const CONTEXT_CLIENT_TYPE: Int = 0x3097; + pub const DISPLAY_SCALING: Int = 10000; + pub const HORIZONTAL_RESOLUTION: Int = 0x3090; + pub const LUMINANCE_BUFFER: Int = 0x308F; + pub const LUMINANCE_SIZE: Int = 0x303D; + pub const OPENGL_ES_BIT: Int = 0x0001; + pub const OPENVG_BIT: Int = 0x0002; + pub const OPENGL_ES_API: Enum = 0x30A0; + pub const OPENVG_API: Enum = 0x30A1; + pub const OPENVG_IMAGE: Int = 0x3096; + pub const PIXEL_ASPECT_RATIO: Int = 0x3092; + pub const RENDERABLE_TYPE: Int = 0x3040; + pub const RENDER_BUFFER: Int = 0x3086; + pub const RGB_BUFFER: Int = 0x308E; + pub const SINGLE_BUFFER: Int = 0x3085; + pub const SWAP_BEHAVIOR: Int = 0x3093; + pub const UNKNOWN: Int = -1; + pub const VERTICAL_RESOLUTION: Int = 0x3091; + + impl<T: api::EGL1_2> Instance<T> { + /// Set the current rendering API. + pub fn bind_api(&self, api: Enum) -> Result<(), Error> { + unsafe { + if self.api.eglBindAPI(api) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Query the current rendering API. + pub fn query_api(&self) -> Enum { + unsafe { self.api.eglQueryAPI() } + } + + /// Create a new EGL pixel buffer surface bound to an OpenVG image. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `NONE`). + pub fn create_pbuffer_from_client_buffer(&self, display: Display, buffer_type: Enum, buffer: ClientBuffer, config: Config, attrib_list: &[Int]) -> Result<Surface, Error> { + check_int_list(attrib_list)?; + unsafe { + let surface = self.api.eglCreatePbufferFromClientBuffer( + display.as_ptr(), + buffer_type, + buffer.as_ptr(), + config.as_ptr(), + attrib_list.as_ptr(), + ); + + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Release EGL per-thread state. + pub fn release_thread(&self) -> Result<(), Error> { + unsafe { + if self.api.eglReleaseThread() == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Complete client API execution prior to subsequent native rendering calls. + pub fn wait_client(&self) -> Result<(), Error> { + unsafe { + if self.api.eglWaitClient() == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + } +} + +#[cfg(feature = "1_2")] +pub use egl1_2::*; + +// ------------------------------------------------------------------------------------------------ +// EGL 1.3 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_3")] +mod egl1_3 { + use super::*; + + pub const CONFORMANT: Int = 0x3042; + pub const CONTEXT_CLIENT_VERSION: Int = 0x3098; + pub const MATCH_NATIVE_PIXMAP: Int = 0x3041; + pub const OPENGL_ES2_BIT: Int = 0x0004; + pub const VG_ALPHA_FORMAT: Int = 0x3088; + pub const VG_ALPHA_FORMAT_NONPRE: Int = 0x308B; + pub const VG_ALPHA_FORMAT_PRE: Int = 0x308C; + pub const VG_ALPHA_FORMAT_PRE_BIT: Int = 0x0040; + pub const VG_COLORSPACE: Int = 0x3087; + pub const VG_COLORSPACE_sRGB: Int = 0x3089; + pub const VG_COLORSPACE_LINEAR: Int = 0x308A; + pub const VG_COLORSPACE_LINEAR_BIT: Int = 0x0020; +} + +#[cfg(feature = "1_3")] +pub use egl1_3::*; + +// ------------------------------------------------------------------------------------------------ +// EGL 1.4 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_4")] +mod egl1_4 { + use super::*; + + pub const DEFAULT_DISPLAY: NativeDisplayType = 0 as NativeDisplayType; + pub const MULTISAMPLE_RESOLVE_BOX_BIT: Int = 0x0200; + pub const MULTISAMPLE_RESOLVE: Int = 0x3099; + pub const MULTISAMPLE_RESOLVE_DEFAULT: Int = 0x309A; + pub const MULTISAMPLE_RESOLVE_BOX: Int = 0x309B; + pub const OPENGL_API: Enum = 0x30A2; + pub const OPENGL_BIT: Int = 0x0008; + pub const SWAP_BEHAVIOR_PRESERVED_BIT: Int = 0x0400; + + impl<T: api::EGL1_4> Instance<T> { + /// Return the current EGL rendering context. + pub fn get_current_context(&self) -> Option<Context> { + unsafe { + let context = self.api.eglGetCurrentContext(); + + if context != NO_CONTEXT { + Some(Context(context)) + } else { + None + } + } + } + } +} + +#[cfg(feature = "1_4")] +pub use egl1_4::*; + +// ------------------------------------------------------------------------------------------------ +// EGL 1.5 +// ------------------------------------------------------------------------------------------------ + +#[cfg(feature = "1_5")] +mod egl1_5 { + use super::*; + + pub type Time = u64; + pub type EGLSync = *mut c_void; + pub type EGLImage = *mut c_void; + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Sync(EGLSync); + + impl Sync { + #[inline] + pub unsafe fn from_ptr(ptr: EGLSync) -> Sync { + Sync(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLSync { + self.0 + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub struct Image(EGLImage); + + impl Image { + #[inline] + pub unsafe fn from_ptr(ptr: EGLImage) -> Image { + Image(ptr) + } + + #[inline] + pub fn as_ptr(&self) -> EGLImage { + self.0 + } + } + + pub const CONTEXT_MAJOR_VERSION: Int = 0x3098; + pub const CONTEXT_MINOR_VERSION: Int = 0x30FB; + pub const CONTEXT_OPENGL_PROFILE_MASK: Int = 0x30FD; + pub const CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY: Int = 0x31BD; + pub const NO_RESET_NOTIFICATION: Int = 0x31BE; + pub const LOSE_CONTEXT_ON_RESET: Int = 0x31BF; + pub const CONTEXT_OPENGL_CORE_PROFILE_BIT: Int = 0x00000001; + pub const CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT: Int = 0x00000002; + pub const CONTEXT_OPENGL_DEBUG: Int = 0x31B0; + pub const CONTEXT_OPENGL_FORWARD_COMPATIBLE: Int = 0x31B1; + pub const CONTEXT_OPENGL_ROBUST_ACCESS: Int = 0x31B2; + pub const OPENGL_ES3_BIT: Int = 0x00000040; + pub const CL_EVENT_HANDLE: Int = 0x309C; + pub const SYNC_CL_EVENT: Int = 0x30FE; + pub const SYNC_CL_EVENT_COMPLETE: Int = 0x30FF; + pub const SYNC_PRIOR_COMMANDS_COMPLETE: Int = 0x30F0; + pub const SYNC_TYPE: Int = 0x30F7; + pub const SYNC_STATUS: Int = 0x30F1; + pub const SYNC_CONDITION: Int = 0x30F8; + pub const SIGNALED: Int = 0x30F2; + pub const UNSIGNALED: Int = 0x30F3; + pub const SYNC_FLUSH_COMMANDS_BIT: Int = 0x0001; + pub const FOREVER: u64 = 0xFFFFFFFFFFFFFFFFu64; + pub const TIMEOUT_EXPIRED: Int = 0x30F5; + pub const CONDITION_SATISFIED: Int = 0x30F6; + pub const NO_SYNC: EGLSync = 0 as EGLSync; + pub const SYNC_FENCE: Int = 0x30F9; + pub const GL_COLORSPACE: Int = 0x309D; + pub const GL_COLORSPACE_SRGB: Int = 0x3089; + pub const GL_COLORSPACE_LINEAR: Int = 0x308A; + pub const GL_RENDERBUFFER: Int = 0x30B9; + pub const GL_TEXTURE_2D: Int = 0x30B1; + pub const GL_TEXTURE_LEVEL: Int = 0x30BC; + pub const GL_TEXTURE_3D: Int = 0x30B2; + pub const GL_TEXTURE_ZOFFSET: Int = 0x30BD; + pub const GL_TEXTURE_CUBE_MAP_POSITIVE_X: Int = 0x30B3; + pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_X: Int = 0x30B4; + pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y: Int = 0x30B5; + pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: Int = 0x30B6; + pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Z: Int = 0x30B7; + pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: Int = 0x30B8; + pub const IMAGE_PRESERVED: Int = 0x30D2; + pub const NO_IMAGE: EGLImage = 0 as EGLImage; + + impl<T: api::EGL1_5> Instance<T> { + /// Create a new EGL sync object. + /// + /// Note that the constant `ATTRIB_NONE` which has the type `Attrib` can be used + /// instead of `NONE` to terminate the attribute list. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `ATTRIB_NONE`). + /// + /// This function is unsafe: when creating an OpenCL Event Sync Object, passing an invalid event + /// handle in `attrib_list` may result in undefined behavior up to and including program + /// termination. + pub unsafe fn create_sync(&self, display: Display, ty: Enum, attrib_list: &[Attrib]) -> Result<Sync, Error> { + check_attrib_list(attrib_list)?; + let sync = self.api.eglCreateSync(display.as_ptr(), ty, attrib_list.as_ptr()); + if sync != NO_SYNC { + Ok(Sync(sync)) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Destroy a sync object. + /// + /// This function is unsafe: if display does not match the display passed to eglCreateSync when + /// sync was created, the behaviour is undefined. + pub unsafe fn destroy_sync(&self, display: Display, sync: Sync) -> Result<(), Error> { + if self.api.eglDestroySync(display.as_ptr(), sync.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Wait in the client for a sync object to be signalled. + /// + /// This function is unsafe: if `display` does not match the [`Display`] passed to [`create_sync`](Self::create_sync) + /// when `sync` was created, the behaviour is undefined. + pub unsafe fn client_wait_sync(&self, display: Display, sync: Sync, flags: Int, timeout: Time) -> Result<Int, Error> { + let status = self.api.eglClientWaitSync(display.as_ptr(), sync.as_ptr(), flags, timeout); + if status != FALSE as Int { + Ok(status) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Return an attribute of a sync object. + /// + /// This function is unsafe: If `display` does not match the [`Display`] passed to [`create_sync`](Self::create_sync) + /// when `sync` was created, behaviour is undefined. + pub unsafe fn get_sync_attrib(&self, display: Display, sync: Sync, attribute: Int) -> Result<Attrib, Error> { + let mut value = 0; + if self.api.eglGetSyncAttrib( + display.as_ptr(), + sync.as_ptr(), + attribute, + &mut value as *mut Attrib, + ) == TRUE + { + Ok(value) + } else { + Err(self.get_error().unwrap()) + } + } + + /// Create a new Image object. + /// + /// Note that the constant `ATTRIB_NONE` which has the type `Attrib` can be used + /// instead of `NONE` to terminate the attribute list. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `ATTRIB_NONE`). + pub fn create_image(&self, display: Display, ctx: Context, target: Enum, buffer: ClientBuffer, attrib_list: &[Attrib]) -> Result<Image, Error> { + check_attrib_list(attrib_list)?; + unsafe { + let image = self.api.eglCreateImage( + display.as_ptr(), + ctx.as_ptr(), + target, + buffer.as_ptr(), + attrib_list.as_ptr(), + ); + if image != NO_IMAGE { + Ok(Image(image)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Destroy an Image object. + pub fn destroy_image(&self, display: Display, image: Image) -> Result<(), Error> { + unsafe { + if self.api.eglDestroyImage(display.as_ptr(), image.as_ptr()) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Return an EGL display connection. + /// + /// Note that the constant `ATTRIB_NONE` which has the type `Attrib` can be used + /// instead of `NONE` to terminate the attribute list. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `ATTRIB_NONE`). + pub fn get_platform_display(&self, platform: Enum, native_display: *mut c_void, attrib_list: &[Attrib]) -> Result<Display, Error> { + check_attrib_list(attrib_list)?; + unsafe { + let display = self.api.eglGetPlatformDisplay(platform, native_display, attrib_list.as_ptr()); + if display != NO_DISPLAY { + Ok(Display(display)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Create a new EGL on-screen rendering surface. + /// + /// Note that the constant `ATTRIB_NONE` which has the type `Attrib` can be used + /// instead of `NONE` to terminate the attribute list. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `ATTRIB_NONE`). + pub fn create_platform_window_surface(&self, display: Display, config: Config, native_window: *mut c_void, attrib_list: &[Attrib]) -> Result<Surface, Error> { + check_attrib_list(attrib_list)?; + unsafe { + let surface = self.api.eglCreatePlatformWindowSurface( + display.as_ptr(), + config.as_ptr(), + native_window, + attrib_list.as_ptr(), + ); + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Create a new EGL offscreen surface. + /// + /// Note that the constant `ATTRIB_NONE` which has the type `Attrib` can be used + /// instead of `NONE` to terminate the attribute list. + /// + /// This will return a `BadParameter` error if `attrib_list` is not a valid + /// attributes list (if it does not terminate with `ATTRIB_NONE`). + pub fn create_platform_pixmap_surface(&self, display: Display, config: Config, native_pixmap: *mut c_void, attrib_list: &[Attrib]) -> Result<Surface, Error> { + check_attrib_list(attrib_list)?; + unsafe { + let surface = self.api.eglCreatePlatformPixmapSurface( + display.as_ptr(), + config.as_ptr(), + native_pixmap, + attrib_list.as_ptr(), + ); + if surface != NO_SURFACE { + Ok(Surface(surface)) + } else { + Err(self.get_error().unwrap()) + } + } + } + + /// Wait in the server for a sync object to be signalled. + /// + /// This function is unsafe: if `display` does not match the [`Display`] passed to [`create_sync`](Self::create_sync) + /// when `sync` was created, the behavior is undefined. + pub fn wait_sync(&self, display: Display, sync: Sync, flags: Int) -> Result<(), Error> { + unsafe { + if self.api.eglWaitSync(display.as_ptr(), sync.as_ptr(), flags) == TRUE { + Ok(()) + } else { + Err(self.get_error().unwrap()) + } + } + } + } +} + +#[cfg(feature = "1_5")] +pub use egl1_5::*; + +// ------------------------------------------------------------------------------------------------- +// FFI +// ------------------------------------------------------------------------------------------------- + +macro_rules! api { + ($($id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* }),*) => { + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + pub enum Version { + $( + #[cfg(feature=$version)] + $id, + )* + } + + impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + $( + #[cfg(feature=$version)] + Version::$id => write!(f, $version), + )* + } + } + } + + pub mod api { + use super::*; + + api!(@api_traits () () $($id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* })*); + } + + #[cfg(feature="static")] + mod ffi { + use libc::{c_char, c_void}; + + use super::{ + Attrib, Boolean, EGLClientBuffer, EGLConfig, EGLContext, EGLDisplay, EGLImage, EGLSurface, + EGLSync, Enum, Int, NativeDisplayType, NativePixmapType, NativeWindowType, Time, + }; + + $( + extern "C" { + $( + #[cfg(feature=$version)] + pub fn $name ($($arg : $atype ),* ) -> $rtype ; + )* + } + )* + } + + #[cfg(feature="static")] + /// Static EGL API interface. + /// + /// This type is only available when the `static` feature is enabled, + /// by statically linking the EGL library at compile time. + #[derive(Copy, Clone, Debug)] + pub struct Static; + + #[cfg(feature="static")] + impl Api for Static { + #[inline(always)] + fn version(&self) -> Version { + LATEST + } + } + + #[cfg(feature="static")] + pub static API: Instance<Static> = Instance::new(Static); + + #[cfg(feature="dynamic")] + extern crate libloading; + + api!(@dynamic_struct $($id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* })*); + api!(@api_types () $($id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* })*); + }; + (@dynamic_struct $($id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* })*) => { + #[cfg(feature="dynamic")] + #[derive(Debug)] + pub enum LoadError<L> { + /// Something wrong happend while loading the library. + Library(L), + + /// The provided version does not meet the requirements. + InvalidVersion { + provided: Version, + required: Version + } + } + + #[cfg(feature="dynamic")] + impl<L: std::error::Error + 'static> std::error::Error for LoadError<L> { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + LoadError::Library(l) => Some(l), + _ => None + } + } + } + + #[cfg(feature="dynamic")] + impl<L: std::fmt::Display> std::fmt::Display for LoadError<L> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + LoadError::Library(l) => write!(f, "Load error: {}", l), + LoadError::InvalidVersion { provided, required } => write!(f, "Invalid EGL API version (required {}, provided {})", required, provided) + } + } + } + + #[cfg(feature="dynamic")] + struct RawDynamic<L> { + lib: L, + version: Version, + $( + $( + #[cfg(feature=$version)] + $name : std::mem::MaybeUninit<unsafe extern "C" fn($($atype ),*) -> $rtype>, + )* + )* + } + + #[cfg(feature="dynamic")] + impl<L> RawDynamic<L> { + #[inline(always)] + /// Returns the underlying EGL library. + pub fn library(&self) -> &L { + &self.lib + } + + #[inline(always)] + /// Returns the EGL version. + pub fn version(&self) -> Version { + self.version + } + + #[inline(always)] + /// Sets the EGL version. + pub unsafe fn set_version(&mut self, version: Version) { + self.version = version + } + + /// Wraps the given library but does not load the symbols. + pub unsafe fn unloaded(lib: L, version: Version) -> Self { + RawDynamic { + lib, + version, + $( + $( + #[cfg(feature=$version)] + $name : std::mem::MaybeUninit::uninit(), + )* + )* + } + } + } + + #[cfg(feature="dynamic")] + /// Dynamic EGL API interface. + /// + /// The first type parameter is the type of the underlying library handle. + /// The second `Dynamic` type parameter gives the EGL API version provided by the library. + /// + /// This type is only available when the `dynamic` feature is enabled. + /// In most cases, you may prefer to directly use the `DynamicInstance` type. + pub struct Dynamic<L, A> { + raw: RawDynamic<L>, + _api_version: std::marker::PhantomData<A> + } + + #[cfg(feature="dynamic")] + impl<L, A> Dynamic<L, A> { + #[inline(always)] + /// Return the underlying EGL library. + pub fn library(&self) -> &L { + self.raw.library() + } + + /// Returns the provided EGL version. + pub fn version(&self) -> Version { + self.raw.version() + } + + /// Wraps the given library but does not load the symbols. + pub(crate) unsafe fn unloaded(lib: L, version: Version) -> Self { + Dynamic { + raw: RawDynamic::unloaded(lib, version), + _api_version: std::marker::PhantomData + } + } + } + + #[cfg(feature="dynamic")] + impl<L, A> Api for Dynamic<L, A> { + /// Returns the provided EGL version. + #[inline(always)] + fn version(&self) -> Version { + self.version() + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature="1_0")] + impl<L: std::borrow::Borrow<libloading::Library>> Dynamic<L, EGL1_0> { + #[inline] + /// Load the EGL API symbols from the given library. + /// + /// This will load the most recent API provided by the library, + /// which is at least EGL 1.0. + /// You can check what version has actually been loaded using [`Dynamic::version`], + /// and/or convert to a more recent version using [`try_into`](TryInto::try_into). + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_from(lib: L) -> Result<Dynamic<L, EGL1_0>, libloading::Error> { + let mut result = Dynamic::unloaded(lib, Version::EGL1_0); + + $( + match $id::load_from(&mut result.raw) { + Ok(()) => result.raw.set_version(Version::$id), + Err(libloading::Error::DlSymUnknown) => { + if Version::$id == Version::EGL1_0 { + return Err(libloading::Error::DlSymUnknown) // we require at least EGL 1.0. + } else { + return Ok(result) + } + }, + Err(libloading::Error::DlSym { desc }) => { + if Version::$id == Version::EGL1_0 { + return Err(libloading::Error::DlSym { desc }) // we require at least EGL 1.0. + } else { + return Ok(result) + } + }, + Err(e) => return Err(e) + } + )* + + Ok(result) + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature="1_0")] + impl<L: std::borrow::Borrow<libloading::Library>> Instance<Dynamic<L, EGL1_0>> { + #[inline(always)] + /// Create an EGL instance using the symbols provided by the given library. + /// + /// The most recent version of EGL provided by the given library is loaded. + /// You can check what version has actually been loaded using [`Instance::version`], + /// and/or convert to a more recent version using [`try_into`](TryInto::try_into). + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_from(lib: L) -> Result<Instance<Dynamic<L, EGL1_0>>, libloading::Error> { + Ok(Instance::new(Dynamic::<L, EGL1_0>::load_from(lib)?)) + } + } + + #[cfg(feature="dynamic")] + impl<L, V> Instance<Dynamic<L, V>> { + /// Cast the API. + #[inline(always)] + pub fn downcast<W>(&self) -> &Instance<Dynamic<L, W>> where Instance<Dynamic<L, V>>: Downcast<Instance<Dynamic<L, W>>> { + Downcast::downcast(self) + } + + /// Cast the API. + #[inline(always)] + pub fn upcast<W>(&self) -> Option<&Instance<Dynamic<L, W>>> where Instance<Dynamic<L, V>>: Upcast<Instance<Dynamic<L, W>>> { + Upcast::upcast(self) + } + } + + #[cfg(feature="dynamic")] + unsafe impl<L: std::borrow::Borrow<libloading::Library> + Send, A: Send> Send for Dynamic<L, A> {} + + #[cfg(feature="dynamic")] + unsafe impl<L: std::borrow::Borrow<libloading::Library> + std::marker::Sync, A: std::marker::Sync> std::marker::Sync for Dynamic<L, A> {} + + #[cfg(feature="dynamic")] + impl<L: std::borrow::Borrow<libloading::Library> + fmt::Debug, A> fmt::Debug for Dynamic<L, A> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Dynamic({:?})", self.library()) + } + } + }; + (@api_traits ( ) ( ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* } $($t_id:ident : $t_version:literal { $(fn $t_name:ident ($($t_arg:ident : $t_atype:ty ),* ) -> $t_rtype:ty ;)* })*) => { + api!(@api_trait ( ) ( ) $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* }); + api!(@api_traits ( $id : $version ) ( : $id ) $($t_id : $t_version { $(fn $t_name ($($t_arg : $t_atype ),* ) -> $t_rtype ;)* })*); + }; + (@api_traits ( $($pred:ident : $p_version:literal)+ ) ( $($deps:tt)+ ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* } $($t_id:ident : $t_version:literal { $(fn $t_name:ident ($($t_arg:ident : $t_atype:ty ),* ) -> $t_rtype:ty ;)* })*) => { + api!(@api_trait ( $($pred : $p_version)* ) ( $($deps)* ) $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* }); + api!(@api_traits ( $($pred : $version)* $id : $version ) ( $($deps)* + $id ) $($t_id : $t_version { $(fn $t_name ($($t_arg : $t_atype ),* ) -> $t_rtype ;)* })*); + }; + (@api_traits ( $($pred:ident : $p_version:literal)* ) ( $($deps:tt)* )) => { + // nothing + }; + (@api_trait ( $($pred:ident : $p_version:literal)* ) ( $($deps:tt)* ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* }) => { + /// EGL API interface. + /// + /// An implementation of this trait can be used to create an [`Instance`]. + /// + /// This crate provides two implemntation of this trait: + /// - [`Static`] which is available with the `static` feature enabled, + /// defined by statically linking to the EGL library at compile time. + /// - [`Dynamic`] which is available with the `dynamic` feature enabled, + /// defined by dynamically linking to the EGL library at runtime. + /// In this case, you may prefer to directly use the `DynamicInstance` type. + #[cfg(feature=$version)] + pub unsafe trait $id $($deps)* { + $( + unsafe fn $name (&self, $($arg : $atype ),* ) -> $rtype ; + )* + } + }; + (@api_types ( ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* } $($t_id:ident : $t_version:literal { $(fn $t_name:ident ($($t_arg:ident : $t_atype:ty ),* ) -> $t_rtype:ty ;)* })*) => { + #[cfg(feature="dynamic")] + $( + #[cfg(not(feature=$t_version))] + )* + #[cfg(feature=$version)] + /// Latest available EGL version. + pub type Latest = $id; + + $( + #[cfg(not(feature=$t_version))] + )* + #[cfg(feature=$version)] + /// Latest available EGL version. + pub const LATEST: Version = Version::$id; + + api!(@api_type ( ) $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* }); + api!(@api_types ( $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* } ) $($t_id : $t_version { $(fn $t_name ($($t_arg : $t_atype ),* ) -> $t_rtype ;)* })*); + }; + (@api_types ( $($pred:ident : $p_version:literal { $(fn $p_name:ident ($($p_arg:ident : $p_atype:ty ),* ) -> $p_rtype:ty ;)* })+ ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* } $($t_id:ident : $t_version:literal { $(fn $t_name:ident ($($t_arg:ident : $t_atype:ty ),* ) -> $t_rtype:ty ;)* })*) => { + #[cfg(feature="dynamic")] + $( + #[cfg(not(feature=$t_version))] + )* + #[cfg(feature=$version)] + /// Latest available EGL version. + pub type Latest = $id; + + $( + #[cfg(not(feature=$t_version))] + )* + #[cfg(feature=$version)] + /// Latest available EGL version. + pub const LATEST: Version = Version::$id; + + api!(@api_type ( $($pred : $p_version { $(fn $p_name ($($p_arg : $p_atype ),* ) -> $p_rtype ;)* })* ) $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* }); + api!(@api_types ( $($pred : $p_version { $(fn $p_name ($($p_arg : $p_atype ),* ) -> $p_rtype ;)* })* $id : $version { $(fn $name ($($arg : $atype ),* ) -> $rtype ;)* } ) $($t_id : $t_version { $(fn $t_name ($($t_arg : $t_atype ),* ) -> $t_rtype ;)* })*); + }; + (@api_types ( $($pred:ident : $p_version:literal { $(fn $p_name:ident ($($p_arg:ident : $p_atype:ty ),* ) -> $p_rtype:ty ;)* })+ ) ) => { + #[cfg(feature="dynamic")] + #[cfg(feature="1_0")] + /// Alias for dynamically linked instances with the latest handled version of EGL. + pub type DynamicInstance<V = Latest> = Instance<Dynamic<libloading::Library, V>>; + + #[cfg(feature="dynamic")] + #[cfg(feature="1_0")] + impl DynamicInstance<EGL1_0> { + #[inline(always)] + /// Create an EGL instance by finding and loading a dynamic library with the given filename. + /// + /// See [`Library::new`](libloading::Library::new) + /// for more details on how the `filename` argument is used. + /// + /// On Linux plateforms, the library is loaded with the `RTLD_NODELETE` flag. + /// See [#14](https://github.com/timothee-haudebourg/khronos-egl/issues/14) for more details. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_from_filename<P: AsRef<std::ffi::OsStr>>(filename: P) -> Result<DynamicInstance<EGL1_0>, libloading::Error> { + #[cfg(target_os = "linux")] + let lib: libloading::Library = { + // On Linux, load library with `RTLD_NOW | RTLD_NODELETE` to fix a SIGSEGV + // See https://github.com/timothee-haudebourg/khronos-egl/issues/14 for more details. + libloading::os::unix::Library::open(Some(filename), 0x2 | 0x1000)?.into() + }; + #[cfg(not(target_os = "linux"))] + let lib = libloading::Library::new(filename)?; + Self::load_from(lib) + } + + #[inline(always)] + /// Create an EGL instance by finding and loading the `libEGL.so.1` or `libEGL.so` library. + /// + /// This is equivalent to `DynamicInstance::load_from_filename("libEGL.so.1")`. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the found library complies to the EGL API. + pub unsafe fn load() -> Result<DynamicInstance<EGL1_0>, libloading::Error> { + Self::load_from_filename("libEGL.so.1").or(Self::load_from_filename("libEGL.so")) + } + } + }; + (@api_type ( $($pred:ident : $p_version:literal { $(fn $p_name:ident ($($p_arg:ident : $p_atype:ty ),* ) -> $p_rtype:ty ;)* })* ) $id:ident : $version:literal { $(fn $name:ident ($($arg:ident : $atype:ty ),* ) -> $rtype:ty ;)* }) => { + #[cfg(feature="static")] + #[cfg(feature=$version)] + unsafe impl api::$id for Static { + $( + #[inline(always)] + unsafe fn $name(&self, $($arg : $atype),*) -> $rtype { + ffi::$name($($arg),*) + } + )* + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + /// EGL version type. + /// + /// Used by [`Dynamic`] to statically know the EGL API version provided by the library. + pub struct $id; + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl $id { + #[allow(unused_variables)] + unsafe fn load_from<L: std::borrow::Borrow<libloading::Library>>(raw: &mut RawDynamic<L>) -> Result<(), libloading::Error> { + let lib = raw.lib.borrow(); + + $( + let name = stringify!($name).as_bytes(); + let symbol = lib.get::<unsafe extern "C" fn($($atype ),*) -> $rtype>(name)?; + let ptr = (&symbol.into_raw().into_raw()) as *const *mut _ as *const unsafe extern "C" fn($($atype ),*) -> $rtype; + assert!(!ptr.is_null()); + raw.$name = std::mem::MaybeUninit::new(*ptr); + )* + + Ok(()) + } + } + + $( + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + unsafe impl<L: std::borrow::Borrow<libloading::Library>> api::$pred for Dynamic<L, $id> { + $( + #[inline(always)] + unsafe fn $p_name(&self, $($p_arg : $p_atype),*) -> $p_rtype { + (self.raw.$p_name.assume_init())($($p_arg),*) + } + )* + } + )* + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + unsafe impl<L: std::borrow::Borrow<libloading::Library>> api::$id for Dynamic<L, $id> { + $( + #[inline(always)] + unsafe fn $name(&self, $($arg : $atype),*) -> $rtype { + (self.raw.$name.assume_init())($($arg),*) + } + )* + } + + $( + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> TryFrom<Dynamic<L, $pred>> for Dynamic<L, $id> { + type Error = Dynamic<L, $pred>; + + fn try_from(other: Dynamic<L, $pred>) -> Result<Self, Dynamic<L, $pred>> { + if other.version() >= Version::$id { + Ok(Dynamic { + raw: other.raw, + _api_version: std::marker::PhantomData + }) + } else { + Err(other) + } + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> From<Dynamic<L, $id>> for Dynamic<L, $pred> { + fn from(other: Dynamic<L, $id>) -> Self { + Dynamic { + raw: other.raw, + _api_version: std::marker::PhantomData + } + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> AsRef<Dynamic<L, $pred>> for Dynamic<L, $id> { + fn as_ref(&self) -> &Dynamic<L, $pred> { + unsafe { std::mem::transmute(self) } // this is safe because both types have the same repr. + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Downcast<Dynamic<L, $pred>> for Dynamic<L, $id> { + fn downcast(&self) -> &Dynamic<L, $pred> { + unsafe { std::mem::transmute(self) } // this is safe because both types have the same repr. + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Downcast<Instance<Dynamic<L, $pred>>> for Instance<Dynamic<L, $id>> { + fn downcast(&self) -> &Instance<Dynamic<L, $pred>> { + unsafe { std::mem::transmute(self) } // this is safe because both types have the same repr. + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Upcast<Dynamic<L, $id>> for Dynamic<L, $pred> { + fn upcast(&self) -> Option<&Dynamic<L, $id>> { + if self.version() >= Version::$id { + Some(unsafe { std::mem::transmute(self) }) // this is safe because both types have the same repr. + } else { + None + } + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Upcast<Instance<Dynamic<L, $id>>> for Instance<Dynamic<L, $pred>> { + fn upcast(&self) -> Option<&Instance<Dynamic<L, $id>>> { + if self.version() >= Version::$id { + Some(unsafe { std::mem::transmute(self) }) // this is safe because both types have the same repr. + } else { + None + } + } + } + )* + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Dynamic<L, $id> { + #[inline] + /// Load the EGL API symbols from the given library. + /// + /// The second `Dynamic` type parameter gives the EGL API version expected to be provided by the library. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_required(lib: L) -> Result<Dynamic<L, $id>, LoadError<libloading::Error>> { + match Dynamic::<L, EGL1_0>::load_from(lib) { + Ok(dynamic) => { + let provided = dynamic.version(); + match dynamic.try_into() { + Ok(t) => Ok(t), + Err(_) => Err(LoadError::InvalidVersion { + provided, + required: Version::$id + }) + } + }, + Err(e) => Err(LoadError::Library(e)) + } + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl<L: std::borrow::Borrow<libloading::Library>> Instance<Dynamic<L, $id>> { + #[inline(always)] + /// Create an EGL instance using the symbols provided by the given library. + /// This function fails if the EGL library does not provide the minimum required version given by the type parameter. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_required_from(lib: L) -> Result<Instance<Dynamic<L, $id>>, LoadError<libloading::Error>> { + Ok(Instance::new(Dynamic::<L, $id>::load_required(lib)?)) + } + } + + #[cfg(feature="dynamic")] + #[cfg(feature=$version)] + impl DynamicInstance<$id> { + #[inline(always)] + /// Create an EGL instance by finding and loading a dynamic library with the given filename. + /// This function fails if the EGL library does not provide the minimum required version given by the type parameter. + /// + /// See [`Library::new`](libloading::Library::new) + /// for more details on how the `filename` argument is used. + /// + /// On Linux plateforms, the library is loaded with the `RTLD_NODELETE` flag. + /// See [#14](https://github.com/timothee-haudebourg/khronos-egl/issues/14) for more details. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the input library complies to the EGL API. + pub unsafe fn load_required_from_filename<P: AsRef<std::ffi::OsStr>>(filename: P) -> Result<DynamicInstance<$id>, LoadError<libloading::Error>> { + #[cfg(target_os = "linux")] + let lib: libloading::Library = { + // On Linux, load library with `RTLD_NOW | RTLD_NODELETE` to fix a SIGSEGV + // See https://github.com/timothee-haudebourg/khronos-egl/issues/14 for more details. + libloading::os::unix::Library::open(Some(filename), 0x2 | 0x1000).map_err(LoadError::Library)?.into() + }; + #[cfg(not(target_os = "linux"))] + let lib = libloading::Library::new(filename).map_err(LoadError::Library)?; + Self::load_required_from(lib) + } + + #[inline(always)] + /// Create an EGL instance by finding and loading the `libEGL.so.1` or `libEGL.so` library. + /// This function fails if the EGL library does not provide the minimum required version given by the type parameter. + /// + /// This is equivalent to `DynamicInstance::load_required_from_filename("libEGL.so.1")`. + /// + /// ## Safety + /// This is fundamentally unsafe since there are no guaranties the found library complies to the EGL API. + pub unsafe fn load_required() -> Result<DynamicInstance<$id>, LoadError<libloading::Error>> { + Self::load_required_from_filename("libEGL.so.1").or(Self::load_required_from_filename("libEGL.so")) + } + } + } +} + +api! { + EGL1_0 : "1_0" { + fn eglChooseConfig( + display: EGLDisplay, + attrib_list: *const Int, + configs: *mut EGLConfig, + config_size: Int, + num_config: *mut Int + ) -> Boolean; + fn eglCopyBuffers( + display: EGLDisplay, + surface: EGLSurface, + target: NativePixmapType + ) -> Boolean; + fn eglCreateContext( + display: EGLDisplay, + config: EGLConfig, + share_context: EGLContext, + attrib_list: *const Int + ) -> EGLContext; + fn eglCreatePbufferSurface( + display: EGLDisplay, + config: EGLConfig, + attrib_list: *const Int + ) -> EGLSurface; + fn eglCreatePixmapSurface( + display: EGLDisplay, + config: EGLConfig, + pixmap: NativePixmapType, + attrib_list: *const Int + ) -> EGLSurface; + fn eglCreateWindowSurface( + display: EGLDisplay, + config: EGLConfig, + win: NativeWindowType, + attrib_list: *const Int + ) -> EGLSurface; + fn eglDestroyContext(display: EGLDisplay, ctx: EGLContext) -> Boolean; + fn eglDestroySurface(display: EGLDisplay, surface: EGLSurface) -> Boolean; + fn eglGetConfigAttrib( + display: EGLDisplay, + config: EGLConfig, + attribute: Int, + value: *mut Int + ) -> Boolean; + fn eglGetConfigs( + display: EGLDisplay, + configs: *mut EGLConfig, + config_size: Int, + num_config: *mut Int + ) -> Boolean; + fn eglGetCurrentDisplay() -> EGLDisplay; + fn eglGetCurrentSurface(readdraw: Int) -> EGLSurface; + fn eglGetDisplay(display_id: NativeDisplayType) -> EGLDisplay; + fn eglGetError() -> Int; + fn eglGetProcAddress(procname: *const c_char) -> extern "C" fn(); + fn eglInitialize(display: EGLDisplay, major: *mut Int, minor: *mut Int) -> Boolean; + fn eglMakeCurrent( + display: EGLDisplay, + draw: EGLSurface, + read: EGLSurface, + ctx: EGLContext + ) -> Boolean; + fn eglQueryContext( + display: EGLDisplay, + ctx: EGLContext, + attribute: Int, + value: *mut Int + ) -> Boolean; + fn eglQueryString(display: EGLDisplay, name: Int) -> *const c_char; + fn eglQuerySurface( + display: EGLDisplay, + surface: EGLSurface, + attribute: Int, + value: *mut Int + ) -> Boolean; + fn eglSwapBuffers(display: EGLDisplay, surface: EGLSurface) -> Boolean; + fn eglTerminate(display: EGLDisplay) -> Boolean; + fn eglWaitGL() -> Boolean; + fn eglWaitNative(engine: Int) -> Boolean; + }, + + EGL1_1 : "1_1" { + fn eglBindTexImage(display: EGLDisplay, surface: EGLSurface, buffer: Int) -> Boolean; + fn eglReleaseTexImage(display: EGLDisplay, surface: EGLSurface, buffer: Int) -> Boolean; + fn eglSurfaceAttrib( + display: EGLDisplay, + surface: EGLSurface, + attribute: Int, + value: Int + ) -> Boolean; + fn eglSwapInterval(display: EGLDisplay, interval: Int) -> Boolean; + }, + + EGL1_2 : "1_2" { + fn eglBindAPI(api: Enum) -> Boolean; + fn eglQueryAPI() -> Enum; + fn eglCreatePbufferFromClientBuffer( + display: EGLDisplay, + buftype: Enum, + buffer: EGLClientBuffer, + config: EGLConfig, + attrib_list: *const Int + ) -> EGLSurface; + fn eglReleaseThread() -> Boolean; + fn eglWaitClient() -> Boolean; + }, + + EGL1_3 : "1_3" { + // nothing. + }, + + EGL1_4 : "1_4" { + fn eglGetCurrentContext() -> EGLContext; + }, + + EGL1_5 : "1_5" { + fn eglCreateSync(display: EGLDisplay, type_: Enum, attrib_list: *const Attrib) -> EGLSync; + fn eglDestroySync(display: EGLDisplay, sync: EGLSync) -> Boolean; + fn eglClientWaitSync(display: EGLDisplay, sync: EGLSync, flags: Int, timeout: Time) -> Int; + fn eglGetSyncAttrib( + display: EGLDisplay, + sync: EGLSync, + attribute: Int, + value: *mut Attrib + ) -> Boolean; + fn eglCreateImage( + display: EGLDisplay, + ctx: EGLContext, + target: Enum, + buffer: EGLClientBuffer, + attrib_list: *const Attrib + ) -> EGLImage; + fn eglDestroyImage(display: EGLDisplay, image: EGLImage) -> Boolean; + fn eglGetPlatformDisplay( + platform: Enum, + native_display: *mut c_void, + attrib_list: *const Attrib + ) -> EGLDisplay; + fn eglCreatePlatformWindowSurface( + display: EGLDisplay, + config: EGLConfig, + native_window: *mut c_void, + attrib_list: *const Attrib + ) -> EGLSurface; + fn eglCreatePlatformPixmapSurface( + display: EGLDisplay, + config: EGLConfig, + native_pixmap: *mut c_void, + attrib_list: *const Attrib + ) -> EGLSurface; + fn eglWaitSync(display: EGLDisplay, sync: EGLSync, flags: Int) -> Boolean; + } +} |