From 221f905a1c5332a96f04918ea87d1c85a05af035 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 Mar 2025 20:05:33 -0700 Subject: [PATCH] pkg/glfw Closes #6702 This removes our mach-glfw dependency and replaces it with an in-tree pkg/glfw that includes both the source for compiling glfw as well as the Zig bindings. This matches the pattern from our other packages. This is based on the upstream mach-glfw work and therefore includes the original license and copyright information. The reasoning is stated in the issue but to summarize for the commit: - mach-glfw is no longer maintained, so we have to take ownership - mach-glfw depended on some large blobs of header files to enable cross-compilation but this isn't something we actually care about, so we can (and do) drop the blobs - mach-glfw blobs were hosted on mach hosts. given mach-glfw is unmaintained, we can't rely on this hosting - mach-glfw relied on a "glfw" package which was owned by another person to be Zig 0.14 compatible, but we no longer need to rely on this - mach-glfw builds were outdated based on latest Zig practices --- .gitattributes | 1 + .github/workflows/test.yml | 128 +- build.zig.zon | 7 +- build.zig.zon.nix | 56 +- build.zig.zon.txt | 9 +- build.zig.zon2json-lock | 35 +- nix/devShell.nix | 3 + pkg/glfw/Cursor.zig | 209 + pkg/glfw/GammaRamp.zig | 74 + pkg/glfw/Image.zig | 82 + pkg/glfw/Joystick.zig | 642 +++ pkg/glfw/LICENSE | 26 + pkg/glfw/Monitor.zig | 599 +++ pkg/glfw/VideoMode.zig | 50 + pkg/glfw/Window.zig | 3551 +++++++++++++++++ pkg/glfw/action.zig | 13 + pkg/glfw/allocator.zig | 143 + pkg/glfw/build.zig | 272 ++ pkg/glfw/build.zig.zon | 15 + pkg/glfw/c.zig | 6 + pkg/glfw/clipboard.zig | 71 + pkg/glfw/errors.zig | 338 ++ pkg/glfw/gamepad_axis.zig | 16 + pkg/glfw/gamepad_button.zig | 37 + pkg/glfw/hat.zig | 100 + pkg/glfw/internal_debug.zig | 14 + pkg/glfw/key.zig | 266 ++ pkg/glfw/main.zig | 586 +++ pkg/glfw/mod.zig | 167 + pkg/glfw/mouse_button.zig | 23 + pkg/glfw/native.zig | 393 ++ pkg/glfw/opengl.zig | 256 ++ pkg/glfw/shims.zig | 84 + pkg/glfw/time.zig | 153 + pkg/glfw/version.zig | 18 + pkg/glfw/vulkan.zig | 290 ++ .../wayland-client-protocol-code.h | 524 +++ ...inhibit-unstable-v1-client-protocol-code.h | 68 + ...idle-inhibit-unstable-v1-client-protocol.h | 232 ++ ...traints-unstable-v1-client-protocol-code.h | 108 + ...-constraints-unstable-v1-client-protocol.h | 667 ++++ ...pointer-unstable-v1-client-protocol-code.h | 79 + ...tive-pointer-unstable-v1-client-protocol.h | 297 ++ .../wayland-viewporter-client-protocol-code.h | 74 + .../wayland-viewporter-client-protocol.h | 398 ++ ...land-xdg-decoration-client-protocol-code.h | 75 + .../wayland-xdg-decoration-client-protocol.h | 378 ++ .../wayland-xdg-shell-client-protocol-code.h | 183 + .../wayland-xdg-shell-client-protocol.h | 2307 +++++++++++ src/build/Config.zig | 9 +- src/build/SharedDeps.zig | 11 +- 51 files changed, 13999 insertions(+), 144 deletions(-) create mode 100644 pkg/glfw/Cursor.zig create mode 100644 pkg/glfw/GammaRamp.zig create mode 100644 pkg/glfw/Image.zig create mode 100644 pkg/glfw/Joystick.zig create mode 100644 pkg/glfw/LICENSE create mode 100644 pkg/glfw/Monitor.zig create mode 100644 pkg/glfw/VideoMode.zig create mode 100644 pkg/glfw/Window.zig create mode 100644 pkg/glfw/action.zig create mode 100644 pkg/glfw/allocator.zig create mode 100644 pkg/glfw/build.zig create mode 100644 pkg/glfw/build.zig.zon create mode 100644 pkg/glfw/c.zig create mode 100644 pkg/glfw/clipboard.zig create mode 100644 pkg/glfw/errors.zig create mode 100644 pkg/glfw/gamepad_axis.zig create mode 100644 pkg/glfw/gamepad_button.zig create mode 100644 pkg/glfw/hat.zig create mode 100644 pkg/glfw/internal_debug.zig create mode 100644 pkg/glfw/key.zig create mode 100644 pkg/glfw/main.zig create mode 100644 pkg/glfw/mod.zig create mode 100644 pkg/glfw/mouse_button.zig create mode 100644 pkg/glfw/native.zig create mode 100644 pkg/glfw/opengl.zig create mode 100644 pkg/glfw/shims.zig create mode 100644 pkg/glfw/time.zig create mode 100644 pkg/glfw/version.zig create mode 100644 pkg/glfw/vulkan.zig create mode 100755 pkg/glfw/wayland-headers/wayland-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol.h create mode 100755 pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol.h create mode 100755 pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol.h create mode 100755 pkg/glfw/wayland-headers/wayland-viewporter-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-viewporter-client-protocol.h create mode 100755 pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol.h create mode 100755 pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol-code.h create mode 100755 pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol.h diff --git a/.gitattributes b/.gitattributes index 7e39ed959..f9ed139eb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,7 @@ vendor/** linguist-vendored website/** linguist-documentation pkg/breakpad/vendor/** linguist-vendored pkg/cimgui/vendor/** linguist-vendored +pkg/glfw/wayland-headers/** linguist-vendored pkg/libintl/config.h linguist-generated=true pkg/libintl/libintl.h linguist-generated=true pkg/simdutf/vendor/** linguist-vendored diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd9b26414..55a6a1732 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,14 +10,15 @@ jobs: name: "Required Checks: Test" runs-on: namespace-profile-ghostty-sm needs: - - build - build-bench + - build-linux - build-linux-libghostty - build-nix - build-snap - build-macos - build-macos-matrix - build-windows + - build-windows-cross - test - test-gtk - test-sentry-linux @@ -48,52 +49,6 @@ jobs: echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}" exit 1 - build: - strategy: - fail-fast: false - matrix: - os: ["namespace-profile-ghostty-md"] - - target: [ - aarch64-linux, - x86_64-linux, - x86-windows-gnu, - x86_64-windows-gnu, - # We don't support cross-compiling to macOS because the macOS build - # requires xcode due to the swift harness. - #aarch64-macos, - #x86_64-macos, - ] - runs-on: ${{ matrix.os }} - needs: test - env: - ZIG_LOCAL_CACHE_DIR: /zig/local-cache - ZIG_GLOBAL_CACHE_DIR: /zig/global-cache - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Cache - uses: namespacelabs/nscloud-cache-action@v1.2.0 - with: - path: | - /nix - /zig - - # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@v30 - with: - nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v15 - with: - name: ghostty - authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - - # Cross-compile the binary. We always use static building for this - # because its the only way to access the headers. - - name: Test Build - run: nix develop -c zig build -Dapp-runtime=glfw -Dtarget=${{ matrix.target }} - build-bench: # We build benchmarks on large because it uses ReleaseFast runs-on: namespace-profile-ghostty-lg @@ -124,6 +79,39 @@ jobs: - name: Build Benchmarks run: nix develop -c zig build -Dapp-runtime=glfw -Demit-bench + build-linux: + strategy: + fail-fast: false + matrix: + os: [namespace-profile-ghostty-md, namespace-profile-ghostty-md-arm64] + runs-on: ${{ matrix.os }} + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@v1.2.0 + with: + path: | + /nix + /zig + + # Install Nix and use that to run our tests so our environment matches exactly. + - uses: cachix/install-nix-action@v30 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v15 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Test Build + run: nix develop -c zig build -Dapp-runtime=glfw + build-linux-libghostty: runs-on: namespace-profile-ghostty-md needs: test @@ -373,6 +361,52 @@ jobs: shell: pwsh run: Get-Content -Path ".\build.log" + build-windows-cross: + strategy: + fail-fast: false + matrix: + os: ["namespace-profile-ghostty-md"] + + target: [ + x86-windows-gnu, + x86_64-windows-gnu, + # We don't support cross-compiling to macOS or Linux because + # we require system libraries. + #aarch64-linux, + #x86_64-linux, + #aarch64-macos, + #x86_64-macos, + ] + runs-on: ${{ matrix.os }} + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@v1.2.0 + with: + path: | + /nix + /zig + + # Install Nix and use that to run our tests so our environment matches exactly. + - uses: cachix/install-nix-action@v30 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v15 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + # Cross-compile the binary. We always use static building for this + # because its the only way to access the headers. + - name: Test Build + run: nix develop -c zig build -Dapp-runtime=glfw -Dtarget=${{ matrix.target }} + test: if: github.repository == 'ghostty-org/ghostty' runs-on: namespace-profile-ghostty-md diff --git a/build.zig.zon b/build.zig.zon index 5c13e4dcf..9a14ce919 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -11,12 +11,6 @@ .url = "https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz", .hash = "libxev-0.0.0-86vtc-ziEgDbLP0vihUn1MhsxNKY4GJEga6BEr7oyHpz", }, - .mach_glfw = .{ - // mitchellh/mach-glfw - .url = "https://github.com/mitchellh/mach-glfw/archive/d84bc11b0601cdad71035a0e9cf21572d76aa0d2.zip", - .hash = "mach_glfw-0.2.0-EJSQm2M9BQCiYGTd9VcKjg2DhSD7WT4kS-MfX68ORRT_", - .lazy = true, - }, .vaxis = .{ // rockorager/libvaxis .url = "git+https://github.com/rockorager/libvaxis#1e24e0dfb509e974e1c8713bcd119d0ae032a8c7", @@ -62,6 +56,7 @@ .cimgui = .{ .path = "./pkg/cimgui" }, .fontconfig = .{ .path = "./pkg/fontconfig" }, .freetype = .{ .path = "./pkg/freetype" }, + .glfw = .{ .path = "./pkg/glfw" }, .gtk4_layer_shell = .{ .path = "./pkg/gtk4-layer-shell" }, .harfbuzz = .{ .path = "./pkg/harfbuzz" }, .highway = .{ .path = "./pkg/highway" }, diff --git a/build.zig.zon.nix b/build.zig.zon.nix index 3f29ad693..9d7e94b40 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -91,54 +91,6 @@ in hash = "sha256-oKZqA9d79jHnp/HsqJWQE33Ffn5Ee5G4VnlQepQuY4o="; }; } - { - name = "mach_glfw-0.2.0-EJSQm2M9BQCiYGTd9VcKjg2DhSD7WT4kS-MfX68ORRT_"; - path = fetchZigArtifact { - name = "mach_glfw"; - url = "https://github.com/mitchellh/mach-glfw/archive/d84bc11b0601cdad71035a0e9cf21572d76aa0d2.zip"; - hash = "sha256-Sh1DvCmawdN+a2JEhNP3wTX43/i5FDlDVOIx7Um/d0U="; - }; - } - { - name = "glfw-0.0.0-bOgnngiqGQCt5HJK25zx1lf9emPYDNtEuQPYmrTCdOoN"; - path = fetchZigArtifact { - name = "glfw"; - url = "git+https://github.com/der-teufel-programming/glfw.git#206deaa2485703ac700d0f3020a8854282aecdbb"; - hash = "sha256-GFeN4J4ZpKvQV8Gw6fxJ+KSpzzdjIYBbO/fTZ0Ooiuk="; - }; - } - { - name = "N-V-__8AABHMqAWYuRdIlflwi8gksPnlUMQBiSxAqQAAZFms"; - path = fetchZigArtifact { - name = "xcode_frameworks"; - url = "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz"; - hash = "sha256-jWMT0p7klpkgX9GOUNAhrR2e6Ej7MobbqT5ZxJrNQoM="; - }; - } - { - name = "N-V-__8AAAIGzwDju1iAEUEqIbBeI3K4JhQ0vBdNzmETnIRQ"; - path = fetchZigArtifact { - name = "vulkan_headers"; - url = "https://pkg.machengine.org/vulkan-headers/53e3ee66a78b97075863135b429956f225b149a5.tar.gz"; - hash = "sha256-kXOn43ntsvxnufobQO0xfzg1cg0R97BmFOU3WRqFsH0="; - }; - } - { - name = "N-V-__8AAJfbCQBWPD1WA6AuYSk8LAIj4Bo_KY-2Br8NEIKT"; - path = fetchZigArtifact { - name = "wayland_headers"; - url = "https://pkg.machengine.org/wayland-headers/7c53e7483c3cfb5c6780ae542c9f5cfa712a826a.tar.gz"; - hash = "sha256-uGIvMyp+xR1jQXTDr6RqYl40Ukiw9l3fW1t6XpYztPY="; - }; - } - { - name = "N-V-__8AACbnQQDnnaLV79Xp3YtkU_g6nseVNOLiA7MzF2a4"; - path = fetchZigArtifact { - name = "x11_headers"; - url = "https://pkg.machengine.org/x11-headers/29aefb525d5c08b05b0351e34b1623854a138c21.tar.gz"; - hash = "sha256-UsbWkOmedS4ygY9C1g7OiPVnTcXzfGKdXImmztYAAiI="; - }; - } { name = "vaxis-0.1.0-BWNV_MHyCAARemSCSwwc3sA1etNgv7ge0BCIXspX6CZv"; path = fetchZigArtifact { @@ -307,6 +259,14 @@ in hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU="; }; } + { + name = "N-V-__8AADTkRwBjUvVwTLOnV96QhN0J5Nyg7YzvnISe-Eax"; + path = fetchZigArtifact { + name = "glfw"; + url = "https://github.com/glfw/glfw/archive/73948e6c0f15b1053cf74b7c4e6b04fd36e97e29.zip"; + hash = "sha256-k7wBKiQpgxBhqHRwSEgZjmfncltlGG8BgY3FhyycM5E="; + }; + } { name = "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr"; path = fetchZigArtifact { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index fb71f4a7a..2279552d7 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -1,8 +1,7 @@ git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc -git+https://github.com/TUSF/zigimg#5102e09be233d372e9e05f4cb2ffbefba30bc1c0 -git+https://github.com/der-teufel-programming/glfw.git#206deaa2485703ac700d0f3020a8854282aecdbb git+https://github.com/rockorager/libvaxis#1e24e0dfb509e974e1c8713bcd119d0ae032a8c7 git+https://github.com/rockorager/libvaxis/?ref=main#6a37605dde55898dcca4769dd3eb1e333959c209 +git+https://github.com/TUSF/zigimg#5102e09be233d372e9e05f4cb2ffbefba30bc1c0 https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz @@ -27,14 +26,10 @@ https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d6 https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz +https://github.com/glfw/glfw/archive/73948e6c0f15b1053cf74b7c4e6b04fd36e97e29.zip https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-11-16-1/ghostty-gobject-0.14.0-2025-03-11-16-1.tar.gz https://github.com/mbadolato/iTerm2-Color-Schemes/archive/e21d5ffd19605741d0e3e19d7c5a8c6c25648673.tar.gz https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz -https://github.com/mitchellh/mach-glfw/archive/d84bc11b0601cdad71035a0e9cf21572d76aa0d2.zip https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz https://github.com/natecraddock/zf/archive/03176fcf23fda543cc02a8675e92c1fe3b1ee2eb.tar.gz https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz -https://pkg.machengine.org/vulkan-headers/53e3ee66a78b97075863135b429956f225b149a5.tar.gz -https://pkg.machengine.org/wayland-headers/7c53e7483c3cfb5c6780ae542c9f5cfa712a826a.tar.gz -https://pkg.machengine.org/x11-headers/29aefb525d5c08b05b0351e34b1623854a138c21.tar.gz -https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz diff --git a/build.zig.zon2json-lock b/build.zig.zon2json-lock index 74fa1c12a..5170d698c 100644 --- a/build.zig.zon2json-lock +++ b/build.zig.zon2json-lock @@ -4,36 +4,6 @@ "url": "https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz", "hash": "sha256-oKZqA9d79jHnp/HsqJWQE33Ffn5Ee5G4VnlQepQuY4o=" }, - "mach_glfw-0.2.0-EJSQm2M9BQCiYGTd9VcKjg2DhSD7WT4kS-MfX68ORRT_": { - "name": "mach_glfw", - "url": "https://github.com/mitchellh/mach-glfw/archive/d84bc11b0601cdad71035a0e9cf21572d76aa0d2.zip", - "hash": "sha256-Sh1DvCmawdN+a2JEhNP3wTX43/i5FDlDVOIx7Um/d0U=" - }, - "glfw-0.0.0-bOgnngiqGQCt5HJK25zx1lf9emPYDNtEuQPYmrTCdOoN": { - "name": "glfw", - "url": "git+https://github.com/der-teufel-programming/glfw.git#206deaa2485703ac700d0f3020a8854282aecdbb", - "hash": "sha256-GFeN4J4ZpKvQV8Gw6fxJ+KSpzzdjIYBbO/fTZ0Ooiuk=" - }, - "N-V-__8AABHMqAWYuRdIlflwi8gksPnlUMQBiSxAqQAAZFms": { - "name": "xcode_frameworks", - "url": "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz", - "hash": "sha256-jWMT0p7klpkgX9GOUNAhrR2e6Ej7MobbqT5ZxJrNQoM=" - }, - "N-V-__8AAAIGzwDju1iAEUEqIbBeI3K4JhQ0vBdNzmETnIRQ": { - "name": "vulkan_headers", - "url": "https://pkg.machengine.org/vulkan-headers/53e3ee66a78b97075863135b429956f225b149a5.tar.gz", - "hash": "sha256-kXOn43ntsvxnufobQO0xfzg1cg0R97BmFOU3WRqFsH0=" - }, - "N-V-__8AAJfbCQBWPD1WA6AuYSk8LAIj4Bo_KY-2Br8NEIKT": { - "name": "wayland_headers", - "url": "https://pkg.machengine.org/wayland-headers/7c53e7483c3cfb5c6780ae542c9f5cfa712a826a.tar.gz", - "hash": "sha256-uGIvMyp+xR1jQXTDr6RqYl40Ukiw9l3fW1t6XpYztPY=" - }, - "N-V-__8AACbnQQDnnaLV79Xp3YtkU_g6nseVNOLiA7MzF2a4": { - "name": "x11_headers", - "url": "https://pkg.machengine.org/x11-headers/29aefb525d5c08b05b0351e34b1623854a138c21.tar.gz", - "hash": "sha256-UsbWkOmedS4ygY9C1g7OiPVnTcXzfGKdXImmztYAAiI=" - }, "vaxis-0.1.0-BWNV_MHyCAARemSCSwwc3sA1etNgv7ge0BCIXspX6CZv": { "name": "vaxis", "url": "git+https://github.com/rockorager/libvaxis#1e24e0dfb509e974e1c8713bcd119d0ae032a8c7", @@ -139,6 +109,11 @@ "url": "https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz", "hash": "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=" }, + "N-V-__8AADTkRwBjUvVwTLOnV96QhN0J5Nyg7YzvnISe-Eax": { + "name": "glfw", + "url": "https://github.com/glfw/glfw/archive/73948e6c0f15b1053cf74b7c4e6b04fd36e97e29.zip", + "hash": "sha256-k7wBKiQpgxBhqHRwSEgZjmfncltlGG8BgY3FhyycM5E=" + }, "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": { "name": "gtk4_layer_shell", "url": "https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz", diff --git a/nix/devShell.nix b/nix/devShell.nix index 67821f896..c6cb84abf 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -41,6 +41,7 @@ harfbuzz, libpng, libGL, + libxkbcommon, libX11, libXcursor, libXext, @@ -83,6 +84,7 @@ glslang spirv-cross + libxkbcommon libX11 libXcursor libXi @@ -162,6 +164,7 @@ in glslang spirv-cross + libxkbcommon libX11 libXcursor libXext diff --git a/pkg/glfw/Cursor.zig b/pkg/glfw/Cursor.zig new file mode 100644 index 000000000..cd79e8848 --- /dev/null +++ b/pkg/glfw/Cursor.zig @@ -0,0 +1,209 @@ +//! Represents a cursor and provides facilities for setting cursor images. + +const std = @import("std"); +const testing = std.testing; + +const c = @import("c.zig").c; +const Image = @import("Image.zig"); + +const internal_debug = @import("internal_debug.zig"); + +const Cursor = @This(); + +ptr: *c.GLFWcursor, + +/// Standard system cursor shapes. +/// +/// These are the standard cursor shapes that can be requested from the platform (window system). +pub const Shape = enum(i32) { + /// The regular arrow cursor shape. + arrow = c.GLFW_ARROW_CURSOR, + + /// The text input I-beam cursor shape. + ibeam = c.GLFW_IBEAM_CURSOR, + + /// The crosshair cursor shape. + crosshair = c.GLFW_CROSSHAIR_CURSOR, + + /// The pointing hand cursor shape. + /// + /// NOTE: This supersedes the old `hand` enum. + pointing_hand = c.GLFW_POINTING_HAND_CURSOR, + + /// The horizontal resize/move arrow shape. + /// + /// The horizontal resize/move arrow shape. This is usually a horizontal double-headed arrow. + // + // NOTE: This supersedes the old `hresize` enum. + resize_ew = c.GLFW_RESIZE_EW_CURSOR, + + /// The vertical resize/move arrow shape. + /// + /// The vertical resize/move shape. This is usually a vertical double-headed arrow. + /// + /// NOTE: This supersedes the old `vresize` enum. + resize_ns = c.GLFW_RESIZE_NS_CURSOR, + + /// The top-left to bottom-right diagonal resize/move arrow shape. + /// + /// The top-left to bottom-right diagonal resize/move shape. This is usually a diagonal + /// double-headed arrow. + /// + /// macos: This shape is provided by a private system API and may fail CursorUnavailable in the + /// future. + /// + /// x11: This shape is provided by a newer standard not supported by all cursor themes. + /// + /// wayland: This shape is provided by a newer standard not supported by all cursor themes. + resize_nwse = c.GLFW_RESIZE_NWSE_CURSOR, + + /// The top-right to bottom-left diagonal resize/move arrow shape. + /// + /// The top-right to bottom-left diagonal resize/move shape. This is usually a diagonal + /// double-headed arrow. + /// + /// macos: This shape is provided by a private system API and may fail with CursorUnavailable + /// in the future. + /// + /// x11: This shape is provided by a newer standard not supported by all cursor themes. + /// + /// wayland: This shape is provided by a newer standard not supported by all cursor themes. + resize_nesw = c.GLFW_RESIZE_NESW_CURSOR, + + /// The omni-directional resize/move cursor shape. + /// + /// The omni-directional resize cursor/move shape. This is usually either a combined horizontal + /// and vertical double-headed arrow or a grabbing hand. + resize_all = c.GLFW_RESIZE_ALL_CURSOR, + + /// The operation-not-allowed shape. + /// + /// The operation-not-allowed shape. This is usually a circle with a diagonal line through it. + /// + /// x11: This shape is provided by a newer standard not supported by all cursor themes. + /// + /// wayland: This shape is provided by a newer standard not supported by all cursor themes. + not_allowed = c.GLFW_NOT_ALLOWED_CURSOR, +}; + +/// Creates a custom cursor. +/// +/// Creates a new custom cursor image that can be set for a window with glfw.Cursor.set. The cursor +/// can be destroyed with glfwCursor.destroy. Any remaining cursors are destroyed by glfw.terminate. +/// +/// The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits per channel with +/// the red channel first. They are arranged canonically as packed sequential rows, starting from +/// the top-left corner. +/// +/// The cursor hotspot is specified in pixels, relative to the upper-left corner of the cursor +/// image. Like all other coordinate systems in GLFW, the X-axis points to the right and the Y-axis +/// points down. +/// +/// @param[in] image The desired cursor image. +/// @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. +/// @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. +/// @return The handle of the created cursor. +/// +/// Possible errors include glfw.ErrorCode.PlatformError and glfw.ErrorCode.InvalidValue +/// null is returned in the event of an error. +/// +/// @pointer_lifetime The specified image data is copied before this function returns. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_object, glfw.Cursor.destroy, glfw.Cursor.createStandard +pub inline fn create(image: Image, xhot: i32, yhot: i32) ?Cursor { + internal_debug.assertInitialized(); + const img = image.toC(); + if (c.glfwCreateCursor(&img, @as(c_int, @intCast(xhot)), @as(c_int, @intCast(yhot)))) |cursor| return Cursor{ .ptr = cursor }; + return null; +} + +/// Creates a cursor with a standard shape. +/// +/// Returns a cursor with a standard shape, that can be set for a window with glfw.Window.setCursor. +/// The images for these cursors come from the system cursor theme and their exact appearance will +/// vary between platforms. +/// +/// Most of these shapes are guaranteed to exist on every supported platform but a few may not be +/// present. See the table below for details. +/// +/// | Cursor shape | Windows | macOS | X11 | Wayland | +/// |------------------|---------|-----------------|-------------------|-------------------| +/// | `.arrow` | Yes | Yes | Yes | Yes | +/// | `.ibeam` | Yes | Yes | Yes | Yes | +/// | `.crosshair` | Yes | Yes | Yes | Yes | +/// | `.pointing_hand` | Yes | Yes | Yes | Yes | +/// | `.resize_ew` | Yes | Yes | Yes | Yes | +/// | `.resize_ns` | Yes | Yes | Yes | Yes | +/// | `.resize_nwse` | Yes | Yes1 | Maybe2 | Maybe2 | +/// | `.resize_nesw` | Yes | Yes1 | Maybe2 | Maybe2 | +/// | `.resize_all` | Yes | Yes | Yes | Yes | +/// | `.not_allowed` | Yes | Yes | Maybe2 | Maybe2 | +/// +/// 1. This uses a private system API and may fail in the future. +/// 2. This uses a newer standard that not all cursor themes support. +/// +/// If the requested shape is not available, this function emits a CursorUnavailable error +/// Possible errors include glfw.ErrorCode.PlatformError and glfw.ErrorCode.CursorUnavailable. +/// null is returned in the event of an error. +/// +/// thread_safety: This function must only be called from the main thread. +/// +/// see also: cursor_object, glfwCreateCursor +pub inline fn createStandard(shape: Shape) ?Cursor { + internal_debug.assertInitialized(); + if (c.glfwCreateStandardCursor(@as(c_int, @intCast(@intFromEnum(shape))))) |cursor| return Cursor{ .ptr = cursor }; + return null; +} + +/// Destroys a cursor. +/// +/// This function destroys a cursor previously created with glfw.Cursor.create. Any remaining +/// cursors will be destroyed by glfw.terminate. +/// +/// If the specified cursor is current for any window, that window will be reverted to the default +/// cursor. This does not affect the cursor mode. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @reentrancy This function must not be called from a callback. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_object, glfw.createCursor +pub inline fn destroy(self: Cursor) void { + internal_debug.assertInitialized(); + c.glfwDestroyCursor(self.ptr); +} + +test "create" { + const allocator = testing.allocator; + + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const image = try Image.init(allocator, 32, 32, 32 * 32 * 4); + defer image.deinit(allocator); + + const cursor = glfw.Cursor.create(image, 0, 0); + if (cursor) |cur| cur.destroy(); +} + +test "createStandard" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const cursor = glfw.Cursor.createStandard(.ibeam); + if (cursor) |cur| cur.destroy(); +} diff --git a/pkg/glfw/GammaRamp.zig b/pkg/glfw/GammaRamp.zig new file mode 100644 index 000000000..7b5a1c3fd --- /dev/null +++ b/pkg/glfw/GammaRamp.zig @@ -0,0 +1,74 @@ +//! Gamma ramp for monitors and related functions. +//! +//! It may be .owned (e.g. in the case of a gamma ramp initialized by you for passing into +//! glfw.Monitor.setGammaRamp) or not .owned (e.g. in the case of one gotten via +//! glfw.Monitor.getGammaRamp.) If it is .owned, deinit should be called to free the memory. It is +//! safe to call deinit even if not .owned. +//! +//! see also: monitor_gamma, glfw.Monitor.getGammaRamp + +const std = @import("std"); +const testing = std.testing; +const mem = std.mem; +const c = @import("c.zig").c; + +const GammaRamp = @This(); + +red: []u16, +green: []u16, +blue: []u16, +owned: ?[]u16, + +/// Initializes a new owned gamma ramp with the given array size and undefined values. +/// +/// see also: glfw.Monitor.getGammaRamp +pub inline fn init(allocator: mem.Allocator, size: usize) !GammaRamp { + const buf = try allocator.alloc(u16, size * 3); + return GammaRamp{ + .red = buf[size * 0 .. (size * 0) + size], + .green = buf[size * 1 .. (size * 1) + size], + .blue = buf[size * 2 .. (size * 2) + size], + .owned = buf, + }; +} + +/// Turns a GLFW / C gamma ramp into the nicer Zig type, and sets `.owned = false`. +/// +/// The returned memory is valid for as long as the GLFW C memory is valid. +pub inline fn fromC(native: c.GLFWgammaramp) GammaRamp { + return GammaRamp{ + .red = native.red[0..native.size], + .green = native.green[0..native.size], + .blue = native.blue[0..native.size], + .owned = null, + }; +} + +/// Turns the nicer Zig type into a GLFW / C gamma ramp, for passing into GLFW C functions. +/// +/// The returned memory is valid for as long as the Zig memory is valid. +pub inline fn toC(self: GammaRamp) c.GLFWgammaramp { + std.debug.assert(self.red.len == self.green.len); + std.debug.assert(self.red.len == self.blue.len); + return c.GLFWgammaramp{ + .red = &self.red[0], + .green = &self.green[0], + .blue = &self.blue[0], + .size = @as(c_uint, @intCast(self.red.len)), + }; +} + +/// Deinitializes the memory using the allocator iff `.owned = true`. +pub inline fn deinit(self: GammaRamp, allocator: mem.Allocator) void { + if (self.owned) |buf| allocator.free(buf); +} + +test "conversion" { + const allocator = testing.allocator; + + const ramp = try GammaRamp.init(allocator, 256); + defer ramp.deinit(allocator); + + const glfw = ramp.toC(); + _ = GammaRamp.fromC(glfw); +} diff --git a/pkg/glfw/Image.zig b/pkg/glfw/Image.zig new file mode 100644 index 000000000..d32e5c310 --- /dev/null +++ b/pkg/glfw/Image.zig @@ -0,0 +1,82 @@ +//! Image data +//! +//! +//! This describes a single 2D image. See the documentation for each related function what the +//! expected pixel format is. +//! +//! see also: cursor_custom, window_icon +//! +//! It may be .owned (e.g. in the case of an image initialized by you for passing into glfw) or not +//! .owned (e.g. in the case of one gotten via glfw) If it is .owned, deinit should be called to +//! free the memory. It is safe to call deinit even if not .owned. + +const std = @import("std"); +const testing = std.testing; +const mem = std.mem; +const c = @import("c.zig").c; + +const Image = @This(); + +/// The width of this image, in pixels. +width: u32, + +/// The height of this image, in pixels. +height: u32, + +/// The pixel data of this image, arranged left-to-right, top-to-bottom. +pixels: []u8, + +/// Whether or not the pixels data is owned by you (true) or GLFW (false). +owned: bool, + +/// Initializes a new owned image with the given size and pixel_data_len of undefined .pixel values. +pub inline fn init(allocator: mem.Allocator, width: u32, height: u32, pixel_data_len: usize) !Image { + const buf = try allocator.alloc(u8, pixel_data_len); + return Image{ + .width = width, + .height = height, + .pixels = buf, + .owned = true, + }; +} + +/// Turns a GLFW / C image into the nicer Zig type, and sets `.owned = false`. +/// +/// The length of pixel data must be supplied, as GLFW's image type does not itself describe the +/// number of bytes required per pixel / the length of the pixel data array. +/// +/// The returned memory is valid for as long as the GLFW C memory is valid. +pub inline fn fromC(native: c.GLFWimage, pixel_data_len: usize) Image { + return Image{ + .width = @as(u32, @intCast(native.width)), + .height = @as(u32, @intCast(native.height)), + .pixels = native.pixels[0..pixel_data_len], + .owned = false, + }; +} + +/// Turns the nicer Zig type into a GLFW / C image, for passing into GLFW C functions. +/// +/// The returned memory is valid for as long as the Zig memory is valid. +pub inline fn toC(self: Image) c.GLFWimage { + return c.GLFWimage{ + .width = @as(c_int, @intCast(self.width)), + .height = @as(c_int, @intCast(self.height)), + .pixels = &self.pixels[0], + }; +} + +/// Deinitializes the memory using the allocator iff `.owned = true`. +pub inline fn deinit(self: Image, allocator: mem.Allocator) void { + if (self.owned) allocator.free(self.pixels); +} + +test "conversion" { + const allocator = testing.allocator; + + const image = try Image.init(allocator, 256, 256, 256 * 256 * 4); + defer image.deinit(allocator); + + const glfw = image.toC(); + _ = Image.fromC(glfw, image.width * image.height * 4); +} diff --git a/pkg/glfw/Joystick.zig b/pkg/glfw/Joystick.zig new file mode 100644 index 000000000..dd55c731d --- /dev/null +++ b/pkg/glfw/Joystick.zig @@ -0,0 +1,642 @@ +//! Represents a Joystick or gamepad +//! +//! It can be manually crafted via e.g. `glfw.Joystick{.jid = .one}`, but more +//! typically you'll want to discover the joystick using `glfw.Joystick.setCallback`. + +const std = @import("std"); + +const c = @import("c.zig").c; +const Window = @import("Window.zig"); +const Action = @import("action.zig").Action; +const GamepadAxis = @import("gamepad_axis.zig").GamepadAxis; +const GamepadButton = @import("gamepad_button.zig").GamepadButton; +const Hat = @import("hat.zig").Hat; + +const internal_debug = @import("internal_debug.zig"); + +const Joystick = @This(); + +/// The GLFW joystick ID. +jid: Id, + +/// Joystick IDs. +/// +/// See glfw.Joystick.setCallback for how these are used. +pub const Id = enum(c_int) { + one = c.GLFW_JOYSTICK_1, + two = c.GLFW_JOYSTICK_2, + three = c.GLFW_JOYSTICK_3, + four = c.GLFW_JOYSTICK_4, + five = c.GLFW_JOYSTICK_5, + six = c.GLFW_JOYSTICK_6, + seven = c.GLFW_JOYSTICK_7, + eight = c.GLFW_JOYSTICK_8, + nine = c.GLFW_JOYSTICK_9, + ten = c.GLFW_JOYSTICK_10, + eleven = c.GLFW_JOYSTICK_11, + twelve = c.GLFW_JOYSTICK_12, + thirteen = c.GLFW_JOYSTICK_13, + fourteen = c.GLFW_JOYSTICK_14, + fifteen = c.GLFW_JOYSTICK_15, + sixteen = c.GLFW_JOYSTICK_16, + pub const last = @as(@This(), @enumFromInt(c.GLFW_JOYSTICK_LAST)); +}; + +/// Gamepad input state +/// +/// This describes the input state of a gamepad. +/// +/// see also: gamepad, glfwGetGamepadState +const GamepadState = extern struct { + /// The states of each gamepad button (see gamepad_buttons), `glfw.Action.press` or `glfw.Action.release`. + /// + /// Use the enumeration helper e.g. `.getButton(.dpad_up)` to access these indices. + buttons: [15]u8, + + /// The states of each gamepad axis (see gamepad_axes), in the range -1.0 to 1.0 inclusive. + /// + /// Use the enumeration helper e.g. `.getAxis(.left_x)` to access these indices. + axes: [6]f32, + + /// Returns the state of the specified gamepad button. + pub fn getButton(self: @This(), which: GamepadButton) Action { + return @as(Action, @enumFromInt(self.buttons[@as(u32, @intCast(@intFromEnum(which)))])); + } + + /// Returns the status of the specified gamepad axis, in the range -1.0 to 1.0 inclusive. + pub fn getAxis(self: @This(), which: GamepadAxis) f32 { + return self.axes[@as(u32, @intCast(@intFromEnum(which)))]; + } +}; + +/// Returns whether the specified joystick is present. +/// +/// This function returns whether the specified joystick is present. +/// +/// There is no need to call this function before other functions that accept a joystick ID, as +/// they all check for presence before performing any other work. +/// +/// @return `true` if the joystick is present, or `false` otherwise. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick +pub inline fn present(self: Joystick) bool { + internal_debug.assertInitialized(); + const is_present = c.glfwJoystickPresent(@intFromEnum(self.jid)); + return is_present == c.GLFW_TRUE; +} + +/// Returns the values of all axes of the specified joystick. +/// +/// This function returns the values of all axes of the specified joystick. Each element in the +/// array is a value between -1.0 and 1.0. +/// +/// If the specified joystick is not present this function will return null but will not generate +/// an error. This can be used instead of first calling glfw.Joystick.present. +/// +/// @return An array of axis values, or null if the joystick is not present. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// null is additionally returned in the event of an error. +/// +/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected or the library is +/// terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick_axis +/// Replaces `glfwGetJoystickPos`. +pub inline fn getAxes(self: Joystick) ?[]const f32 { + internal_debug.assertInitialized(); + var count: c_int = undefined; + const axes = c.glfwGetJoystickAxes(@intFromEnum(self.jid), &count); + if (axes == null) return null; + return axes[0..@as(u32, @intCast(count))]; +} + +/// Returns the state of all buttons of the specified joystick. +/// +/// This function returns the state of all buttons of the specified joystick. Each element in the +/// array is either `glfw.Action.press` or `glfw.Action.release`. +/// +/// For backward compatibility with earlier versions that did not have glfw.Joystick.getHats, the +/// button array also includes all hats, each represented as four buttons. The hats are in the same +/// order as returned by glfw.Joystick.getHats and are in the order _up_, _right_, _down_ and +/// _left_. To disable these extra buttons, set the glfw.joystick_hat_buttons init hint before +/// initialization. +/// +/// If the specified joystick is not present this function will return null but will not generate an +/// error. This can be used instead of first calling glfw.Joystick.present. +/// +/// @return An array of button states, or null if the joystick is not present. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// null is additionally returned in the event of an error. +/// +/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick_button +pub inline fn getButtons(self: Joystick) ?[]const u8 { + internal_debug.assertInitialized(); + var count: c_int = undefined; + const buttons = c.glfwGetJoystickButtons(@intFromEnum(self.jid), &count); + if (buttons == null) return null; + return buttons[0..@as(u32, @intCast(count))]; +} + +/// Returns the state of all hats of the specified joystick. +/// +/// This function returns the state of all hats of the specified joystick. Each element in the array +/// is one of the following values: +/// +/// | Name | Value | +/// |---------------------------|---------------------------------------------| +/// | `glfw.RawHats.centered` | 0 | +/// | `glfw.RawHats.up` | 1 | +/// | `glfw.RawHats.right` | 2 | +/// | `glfw.RawHats.down` | 4 | +/// | `glfw.RawHats.left` | 8 | +/// | `glfw.RawHats.right_up` | `glfw.RawHats.right` \| `glfw.RawHats.up` | +/// | `glfw.RawHats.right_down` | `glfw.RawHats.right` \| `glfw.RawHats.down` | +/// | `glfw.RawHats.left_up` | `glfw.RawHats.left` \| `glfw.RawHats.up` | +/// | `glfw.RawHats.left_down` | `glfw.RawHats.left` \| `glfw.RawHats.down` | +/// +/// The diagonal directions are bitwise combinations of the primary (up, right, down and left) +/// directions, since the Zig GLFW wrapper returns a packed struct it is trivial to test for these: +/// +/// ``` +/// if (hats.up and hats.right) { +/// // up-right! +/// } +/// ``` +/// +/// If the specified joystick is not present this function will return null but will not generate an +/// error. This can be used instead of first calling glfw.Joystick.present. +/// +/// @return An array of hat states, or null if the joystick is not present. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// null is additionally returned in the event of an error. +/// +/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected, this function is called +/// again for that joystick or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick_hat +pub inline fn getHats(self: Joystick) ?[]const Hat { + internal_debug.assertInitialized(); + var count: c_int = undefined; + const hats = c.glfwGetJoystickHats(@intFromEnum(self.jid), &count); + if (hats == null) return null; + const slice = hats[0..@as(u32, @intCast(count))]; + return @as(*const []const Hat, @ptrCast(&slice)).*; +} + +/// Returns the name of the specified joystick. +/// +/// This function returns the name, encoded as UTF-8, of the specified joystick. The returned string +/// is allocated and freed by GLFW. You should not free it yourself. +/// +/// If the specified joystick is not present this function will return null but will not generate an +/// error. This can be used instead of first calling glfw.Joystick.present. +/// +/// @return The UTF-8 encoded name of the joystick, or null if the joystick is not present or an +/// error occurred. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// null is additionally returned in the event of an error. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick_name +pub inline fn getName(self: Joystick) ?[:0]const u8 { + internal_debug.assertInitialized(); + const name_opt = c.glfwGetJoystickName(@intFromEnum(self.jid)); + return if (name_opt) |name| + std.mem.span(@as([*:0]const u8, @ptrCast(name))) + else + null; +} + +/// Returns the SDL compatible GUID of the specified joystick. +/// +/// This function returns the SDL compatible GUID, as a UTF-8 encoded hexadecimal string, of the +/// specified joystick. The returned string is allocated and freed by GLFW. You should not free it +/// yourself. +/// +/// The GUID is what connects a joystick to a gamepad mapping. A connected joystick will always have +/// a GUID even if there is no gamepad mapping assigned to it. +/// +/// If the specified joystick is not present this function will return null but will not generate an +/// error. This can be used instead of first calling glfw.Joystick.present. +/// +/// The GUID uses the format introduced in SDL 2.0.5. This GUID tries to uniquely identify the make +/// and model of a joystick but does not identify a specific unit, e.g. all wired Xbox 360 +/// controllers will have the same GUID on that platform. The GUID for a unit may vary between +/// platforms depending on what hardware information the platform specific APIs provide. +/// +/// @return The UTF-8 encoded GUID of the joystick, or null if the joystick is not present. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// null is additionally returned in the event of an error. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: gamepad +pub inline fn getGUID(self: Joystick) ?[:0]const u8 { + internal_debug.assertInitialized(); + const guid_opt = c.glfwGetJoystickGUID(@intFromEnum(self.jid)); + return if (guid_opt) |guid| + std.mem.span(@as([*:0]const u8, @ptrCast(guid))) + else + null; +} + +/// Sets the user pointer of the specified joystick. +/// +/// This function sets the user-defined pointer of the specified joystick. The current value is +/// retained until the joystick is disconnected. The initial value is null. +/// +/// This function may be called from the joystick callback, even for a joystick that is being disconnected. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: joystick_userptr, glfw.Joystick.getUserPointer +pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void { + internal_debug.assertInitialized(); + c.glfwSetJoystickUserPointer(@intFromEnum(self.jid), @as(*anyopaque, @ptrCast(pointer))); +} + +/// Returns the user pointer of the specified joystick. +/// +/// This function returns the current value of the user-defined pointer of the specified joystick. +/// The initial value is null. +/// +/// This function may be called from the joystick callback, even for a joystick that is being +/// disconnected. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: joystick_userptr, glfw.Joystick.setUserPointer +pub inline fn getUserPointer(self: Joystick, comptime PointerType: type) ?PointerType { + internal_debug.assertInitialized(); + const ptr = c.glfwGetJoystickUserPointer(@intFromEnum(self.jid)); + if (ptr) |p| return @as(PointerType, @ptrCast(@alignCast(p))); + return null; +} + +/// Describes an event relating to a joystick. +pub const Event = enum(c_int) { + /// The device was connected. + connected = c.GLFW_CONNECTED, + + /// The device was disconnected. + disconnected = c.GLFW_DISCONNECTED, +}; + +/// Sets the joystick configuration callback. +/// +/// This function sets the joystick configuration callback, or removes the currently set callback. +/// This is called when a joystick is connected to or disconnected from the system. +/// +/// For joystick connection and disconnection events to be delivered on all platforms, you need to +/// call one of the event processing (see events) functions. Joystick disconnection may also be +/// detected and the callback called by joystick functions. The function will then return whatever +/// it returns if the joystick is not present. +/// +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param `jid` The joystick that was connected or disconnected. +/// @callback_param `event` One of `.connected` or `.disconnected`. Future releases may add +/// more events. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: joystick_event +pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Event) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + Joystick{ .jid = @as(Joystick.Id, @enumFromInt(jid)) }, + @as(Event, @enumFromInt(event)), + }); + } + }; + + if (c.glfwSetJoystickCallback(CWrapper.joystickCallbackWrapper) != null) return; + } else { + if (c.glfwSetJoystickCallback(null) != null) return; + } +} + +/// Adds the specified SDL_GameControllerDB gamepad mappings. +/// +/// This function parses the specified ASCII encoded string and updates the internal list with any +/// gamepad mappings it finds. This string may contain either a single gamepad mapping or many +/// mappings separated by newlines. The parser supports the full format of the `gamecontrollerdb.txt` +/// source file including empty lines and comments. +/// +/// See gamepad_mapping for a description of the format. +/// +/// If there is already a gamepad mapping for a given GUID in the internal list, it will be +/// replaced by the one passed to this function. If the library is terminated and re-initialized +/// the internal list will revert to the built-in default. +/// +/// @param[in] string The string containing the gamepad mappings. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue. +/// Returns a boolean indicating success. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: gamepad, glfw.Joystick.isGamepad, glfwGetGamepadName +/// +/// +/// @ingroup input +pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) bool { + internal_debug.assertInitialized(); + return c.glfwUpdateGamepadMappings(gamepad_mappings) == c.GLFW_TRUE; +} + +/// Returns whether the specified joystick has a gamepad mapping. +/// +/// This function returns whether the specified joystick is both present and has a gamepad mapping. +/// +/// If the specified joystick is present but does not have a gamepad mapping this function will +/// return `false` but will not generate an error. Call glfw.Joystick.present to check if a +/// joystick is present regardless of whether it has a mapping. +/// +/// @return `true` if a joystick is both present and has a gamepad mapping, or `false` otherwise. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum. +/// Additionally returns false in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: gamepad, glfw.Joystick.getGamepadState +pub inline fn isGamepad(self: Joystick) bool { + internal_debug.assertInitialized(); + const is_gamepad = c.glfwJoystickIsGamepad(@intFromEnum(self.jid)); + return is_gamepad == c.GLFW_TRUE; +} + +/// Returns the human-readable gamepad name for the specified joystick. +/// +/// This function returns the human-readable name of the gamepad from the gamepad mapping assigned +/// to the specified joystick. +/// +/// If the specified joystick is not present or does not have a gamepad mapping this function will +/// return null, not an error. Call glfw.Joystick.present to check whether it is +/// present regardless of whether it has a mapping. +/// +/// @return The UTF-8 encoded name of the gamepad, or null if the joystick is not present or does +/// not have a mapping. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum. +/// Additionally returns null in the event of an error. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified joystick is disconnected, the gamepad mappings are +/// updated or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: gamepad, glfw.Joystick.isGamepad +pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 { + internal_debug.assertInitialized(); + const name_opt = c.glfwGetGamepadName(@intFromEnum(self.jid)); + return if (name_opt) |name| + std.mem.span(@as([*:0]const u8, @ptrCast(name))) + else + null; +} + +/// Retrieves the state of the joystick remapped as a gamepad. +/// +/// This function retrieves the state of the joystick remapped to an Xbox-like gamepad. +/// +/// If the specified joystick is not present or does not have a gamepad mapping this function will +/// return `false`. Call glfw.joystickPresent to check whether it is present regardless of whether +/// it has a mapping. +/// +/// The Guide button may not be available for input as it is often hooked by the system or the +/// Steam client. +/// +/// Not all devices have all the buttons or axes provided by GamepadState. Unavailable buttons +/// and axes will always report `glfw.Action.release` and 0.0 respectively. +/// +/// @param[in] jid The joystick (see joysticks) to query. +/// @param[out] state The gamepad input state of the joystick. +/// @return the gamepad input state if successful, or null if no joystick is connected or it has no +/// gamepad mapping. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum. +/// Returns null in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: gamepad, glfw.UpdateGamepadMappings, glfw.Joystick.isGamepad +pub inline fn getGamepadState(self: Joystick) ?GamepadState { + internal_debug.assertInitialized(); + var state: GamepadState = undefined; + const success = c.glfwGetGamepadState(@intFromEnum(self.jid), @as(*c.GLFWgamepadstate, @ptrCast(&state))); + return if (success == c.GLFW_TRUE) state else null; +} + +test "present" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.present(); +} + +test "getAxes" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getAxes(); +} + +test "getButtons" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getButtons(); +} + +test "getHats" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + + if (joystick.getHats()) |hats| { + for (hats) |hat| { + if (hat.down and hat.up) { + // down-up! + } + } + } +} + +test "getName" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getName(); +} + +test "getGUID" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getGUID(); +} + +test "setUserPointer_syntax" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + + // Must be called from joystick callback, we cannot test it. + _ = joystick; + _ = setUserPointer; +} + +test "getUserPointer_syntax" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + + // Must be called from joystick callback, we cannot test it. + _ = joystick; + _ = getUserPointer; +} + +test "setCallback" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + glfw.Joystick.setCallback((struct { + pub fn callback(joystick: Joystick, event: Event) void { + _ = joystick; + _ = event; + } + }).callback); +} + +test "updateGamepadMappings_syntax" { + // We don't have a gamepad mapping to test with, just confirm the syntax is good. + _ = updateGamepadMappings; +} + +test "isGamepad" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.isGamepad(); +} + +test "getGamepadName" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getGamepadName(); +} + +test "getGamepadState" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const joystick = glfw.Joystick{ .jid = .one }; + _ = joystick.getGamepadState(); + _ = (std.mem.zeroes(GamepadState)).getAxis(.left_x); + _ = (std.mem.zeroes(GamepadState)).getButton(.dpad_up); +} diff --git a/pkg/glfw/LICENSE b/pkg/glfw/LICENSE new file mode 100644 index 000000000..eeeb852fe --- /dev/null +++ b/pkg/glfw/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2021 Hexops Contributors (given via the Git commit history). +Copyright (c) 2025 Mitchell Hashimoto + +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/pkg/glfw/Monitor.zig b/pkg/glfw/Monitor.zig new file mode 100644 index 000000000..868872e19 --- /dev/null +++ b/pkg/glfw/Monitor.zig @@ -0,0 +1,599 @@ +//! Monitor type and related functions + +const std = @import("std"); +const mem = std.mem; +const testing = std.testing; +const c = @import("c.zig").c; + +const GammaRamp = @import("GammaRamp.zig"); +const VideoMode = @import("VideoMode.zig"); + +const internal_debug = @import("internal_debug.zig"); + +const Monitor = @This(); + +handle: *c.GLFWmonitor, + +/// A monitor position, in screen coordinates, of the upper left corner of the monitor on the +/// virtual screen. +const Pos = struct { + /// The x coordinate. + x: u32, + /// The y coordinate. + y: u32, +}; + +/// Returns the position of the monitor's viewport on the virtual screen. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_properties +pub inline fn getPos(self: Monitor) Pos { + internal_debug.assertInitialized(); + var xpos: c_int = 0; + var ypos: c_int = 0; + c.glfwGetMonitorPos(self.handle, &xpos, &ypos); + return Pos{ .x = @as(u32, @intCast(xpos)), .y = @as(u32, @intCast(ypos)) }; +} + +/// The monitor workarea, in screen coordinates. +/// +/// This is the position of the upper-left corner of the work area of the monitor, along with the +/// work area size. The work area is defined as the area of the monitor not occluded by the +/// window system task bar where present. If no task bar exists then the work area is the +/// monitor resolution in screen coordinates. +const Workarea = struct { + x: u32, + y: u32, + width: u32, + height: u32, +}; + +/// Retrieves the work area of the monitor. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// A zero value is returned in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_workarea +pub inline fn getWorkarea(self: Monitor) Workarea { + internal_debug.assertInitialized(); + var xpos: c_int = 0; + var ypos: c_int = 0; + var width: c_int = 0; + var height: c_int = 0; + c.glfwGetMonitorWorkarea(self.handle, &xpos, &ypos, &width, &height); + return Workarea{ .x = @as(u32, @intCast(xpos)), .y = @as(u32, @intCast(ypos)), .width = @as(u32, @intCast(width)), .height = @as(u32, @intCast(height)) }; +} + +/// The physical size, in millimetres, of the display area of a monitor. +const PhysicalSize = struct { + width_mm: u32, + height_mm: u32, +}; + +/// Returns the physical size of the monitor. +/// +/// Some platforms do not provide accurate monitor size information, either because the monitor +/// [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) +/// data is incorrect or because the driver does not report it accurately. +/// +/// win32: On Windows 8 and earlier the physical size is calculated from +/// the current resolution and system DPI instead of querying the monitor EDID data +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_properties +pub inline fn getPhysicalSize(self: Monitor) PhysicalSize { + internal_debug.assertInitialized(); + var width_mm: c_int = 0; + var height_mm: c_int = 0; + c.glfwGetMonitorPhysicalSize(self.handle, &width_mm, &height_mm); + return PhysicalSize{ .width_mm = @as(u32, @intCast(width_mm)), .height_mm = @as(u32, @intCast(height_mm)) }; +} + +/// The content scale for a monitor. +/// +/// This is the ratio between the current DPI and the platform's default DPI. This is especially +/// important for text and any UI elements. If the pixel dimensions of your UI scaled by this look +/// appropriate on your machine then it should appear at a reasonable size on other machines +/// regardless of their DPI and scaling settings. This relies on the system DPI and scaling +/// settings being somewhat correct. +/// +/// The content scale may depend on both the monitor resolution and pixel density and on users +/// settings. It may be very different from the raw DPI calculated from the physical size and +/// current resolution. +const ContentScale = struct { + x_scale: f32, + y_scale: f32, +}; + +/// Returns the content scale for the monitor. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// A zero value is returned in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_scale, glfw.Window.getContentScale +pub inline fn getContentScale(self: Monitor) ContentScale { + internal_debug.assertInitialized(); + var x_scale: f32 = 0; + var y_scale: f32 = 0; + c.glfwGetMonitorContentScale(self.handle, &x_scale, &y_scale); + return ContentScale{ .x_scale = @as(f32, @floatCast(x_scale)), .y_scale = @as(f32, @floatCast(y_scale)) }; +} + +/// Returns the name of the specified monitor. +/// +/// This function returns a human-readable name, encoded as UTF-8, of the specified monitor. The +/// name typically reflects the make and model of the monitor and is not guaranteed to be unique +/// among the connected monitors. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the specified monitor is disconnected or the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_properties +pub inline fn getName(self: Monitor) [*:0]const u8 { + internal_debug.assertInitialized(); + if (c.glfwGetMonitorName(self.handle)) |name| return @as([*:0]const u8, @ptrCast(name)); + // `glfwGetMonitorName` returns `null` only for errors, but the only error is unreachable + // (NotInitialized) + unreachable; +} + +/// Sets the user pointer of the specified monitor. +/// +/// This function sets the user-defined pointer of the specified monitor. The current value is +/// retained until the monitor is disconnected. +/// +/// This function may be called from the monitor callback, even for a monitor that is being +/// disconnected. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: monitor_userptr, glfw.Monitor.getUserPointer +pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void { + internal_debug.assertInitialized(); + c.glfwSetMonitorUserPointer(self.handle, ptr); +} + +/// Returns the user pointer of the specified monitor. +/// +/// This function returns the current value of the user-defined pointer of the specified monitor. +/// +/// This function may be called from the monitor callback, even for a monitor that is being +/// disconnected. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: monitor_userptr, glfw.Monitor.setUserPointer +pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T { + internal_debug.assertInitialized(); + const ptr = c.glfwGetMonitorUserPointer(self.handle); + if (ptr == null) return null; + return @as(*T, @ptrCast(@alignCast(ptr.?))); +} + +/// Returns the available video modes for the specified monitor. +/// +/// This function returns an array of all video modes supported by the monitor. The returned slice +/// is sorted in ascending order, first by color bit depth (the sum of all channel depths) and +/// then by resolution area (the product of width and height), then resolution width and finally +/// by refresh rate. +/// +/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable. +/// Returns null in the event of an error. +/// +/// The returned slice memory is owned by the caller. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_modes, glfw.Monitor.getVideoMode +/// +/// wayland: Gamma handling is privileged protocol, this function will thus never be implemented and +/// emits glfw.ErrorCode.FeatureUnavailable +/// +/// TODO(glfw): rewrite this to not require any allocation. +pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) mem.Allocator.Error!?[]VideoMode { + internal_debug.assertInitialized(); + var count: c_int = 0; + if (c.glfwGetVideoModes(self.handle, &count)) |modes| { + const slice = try allocator.alloc(VideoMode, @as(u32, @intCast(count))); + var i: u32 = 0; + while (i < count) : (i += 1) { + slice[i] = VideoMode{ .handle = @as([*c]const c.GLFWvidmode, @ptrCast(modes))[i] }; + } + return slice; + } + return null; +} + +/// Returns the current mode of the specified monitor. +/// +/// This function returns the current video mode of the specified monitor. If you have created a +/// full screen window for that monitor, the return value will depend on whether that window is +/// iconified. +/// +/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable. +/// Additionally returns null in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented +/// and will thus never be implemented and emits glfw.ErrorCode.FeatureUnavailable +/// +/// see also: monitor_modes, glfw.Monitor.getVideoModes +pub inline fn getVideoMode(self: Monitor) ?VideoMode { + internal_debug.assertInitialized(); + if (c.glfwGetVideoMode(self.handle)) |mode| return VideoMode{ .handle = mode.* }; + return null; +} + +/// Generates a gamma ramp and sets it for the specified monitor. +/// +/// This function generates an appropriately sized gamma ramp from the specified exponent and then +/// calls glfw.Monitor.setGammaRamp with it. The value must be a finite number greater than zero. +/// +/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction, +/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly +/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior. +/// +/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint. +/// +/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable. +/// +/// wayland: Gamma handling is privileged protocol, this function will thus never be implemented and +/// emits glfw.ErrorCode.FeatureUnavailable +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_gamma +pub inline fn setGamma(self: Monitor, gamma: f32) void { + internal_debug.assertInitialized(); + + std.debug.assert(!std.math.isNan(gamma)); + std.debug.assert(gamma >= 0); + std.debug.assert(gamma <= std.math.f32_max); + + c.glfwSetGamma(self.handle, gamma); +} + +/// Returns the current gamma ramp for the specified monitor. +/// +/// This function returns the current gamma ramp of the specified monitor. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns null in the event of an error. +/// +/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented +/// and returns glfw.ErrorCode.FeatureUnavailable. +/// +/// The returned gamma ramp is `.owned = true` by GLFW, and is valid until the monitor is +/// disconnected, this function is called again, or `glfw.terminate()` is called. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_gamma +pub inline fn getGammaRamp(self: Monitor) ?GammaRamp { + internal_debug.assertInitialized(); + if (c.glfwGetGammaRamp(self.handle)) |ramp| return GammaRamp.fromC(ramp.*); + return null; +} + +/// Sets the current gamma ramp for the specified monitor. +/// +/// This function sets the current gamma ramp for the specified monitor. The original gamma ramp +/// for that monitor is saved by GLFW the first time this function is called and is restored by +/// `glfw.terminate()`. +/// +/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction, +/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly +/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior. +/// +/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint. +/// +/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable. +/// +/// The size of the specified gamma ramp should match the size of the current ramp for that +/// monitor. On win32, the gamma ramp size must be 256. +/// +/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented +/// and returns glfw.ErrorCode.FeatureUnavailable. +/// +/// @pointer_lifetime The specified gamma ramp is copied before this function returns. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_gamma +pub inline fn setGammaRamp(self: Monitor, ramp: GammaRamp) void { + internal_debug.assertInitialized(); + c.glfwSetGammaRamp(self.handle, &ramp.toC()); +} + +/// Returns the currently connected monitors. +/// +/// This function returns a slice of all currently connected monitors. The primary monitor is +/// always first. If no monitors were found, this function returns an empty slice. +/// +/// The returned slice memory is owned by the caller. The underlying handles are owned by GLFW, and +/// are valid until the monitor configuration changes or `glfw.terminate` is called. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_monitors, monitor_event, glfw.monitor.getPrimary +pub inline fn getAll(allocator: mem.Allocator) mem.Allocator.Error![]Monitor { + internal_debug.assertInitialized(); + var count: c_int = 0; + if (c.glfwGetMonitors(&count)) |monitors| { + const slice = try allocator.alloc(Monitor, @as(u32, @intCast(count))); + var i: u32 = 0; + while (i < count) : (i += 1) { + slice[i] = Monitor{ .handle = @as([*c]const ?*c.GLFWmonitor, @ptrCast(monitors))[i].? }; + } + return slice; + } + // `glfwGetMonitors` returning null can be either an error or no monitors, but the only error is + // unreachable (NotInitialized) + return &[_]Monitor{}; +} + +/// Returns the primary monitor. +/// +/// This function returns the primary monitor. This is usually the monitor where elements like +/// the task bar or global menu bar are located. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_monitors, glfw.monitors.getAll +pub inline fn getPrimary() ?Monitor { + internal_debug.assertInitialized(); + if (c.glfwGetPrimaryMonitor()) |handle| return Monitor{ .handle = handle }; + return null; +} + +/// Describes an event relating to a monitor. +pub const Event = enum(c_int) { + /// The device was connected. + connected = c.GLFW_CONNECTED, + + /// The device was disconnected. + disconnected = c.GLFW_DISCONNECTED, +}; + +/// Sets the monitor configuration callback. +/// +/// This function sets the monitor configuration callback, or removes the currently set callback. +/// This is called when a monitor is connected to or disconnected from the system. Example: +/// +/// ``` +/// fn monitorCallback(monitor: glfw.Monitor, event: glfw.Monitor.Event, data: *MyData) void { +/// // data is the pointer you passed into setCallback. +/// // event is one of .connected or .disconnected +/// } +/// ... +/// glfw.Monitor.setCallback(MyData, &myData, monitorCallback) +/// ``` +/// +/// `event` may be one of .connected or .disconnected. More events may be added in the future. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: monitor_event +pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + Monitor{ .handle = monitor.? }, + @as(Event, @enumFromInt(event)), + }); + } + }; + + if (c.glfwSetMonitorCallback(CWrapper.monitorCallbackWrapper) != null) return; + } else { + if (c.glfwSetMonitorCallback(null) != null) return; + } +} + +test "getAll" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const allocator = testing.allocator; + const monitors = try getAll(allocator); + defer allocator.free(monitors); +} + +test "getPrimary" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = getPrimary(); +} + +test "getPos" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getPos(); + } +} + +test "getWorkarea" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getWorkarea(); + } +} + +test "getPhysicalSize" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getPhysicalSize(); + } +} + +test "getContentScale" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getContentScale(); + } +} + +test "getName" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getName(); + } +} + +test "userPointer" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + var p = m.getUserPointer(u32); + try testing.expect(p == null); + var x: u32 = 5; + m.setUserPointer(u32, &x); + p = m.getUserPointer(u32); + try testing.expectEqual(p.?.*, 5); + } +} + +test "setCallback" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + setCallback(struct { + fn callback(monitor: Monitor, event: Event) void { + _ = monitor; + _ = event; + } + }.callback); +} + +test "getVideoModes" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + const allocator = testing.allocator; + const modes_maybe = try m.getVideoModes(allocator); + if (modes_maybe) |modes| { + defer allocator.free(modes); + } + } +} + +test "getVideoMode" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + _ = m.getVideoMode(); + } +} + +test "set_getGammaRamp" { + const allocator = testing.allocator; + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const monitor = getPrimary(); + if (monitor) |m| { + if (m.getGammaRamp()) |ramp| { + // Set it to the exact same value; if we do otherwise an our tests fail it wouldn't call + // terminate and our made-up gamma ramp would get stuck. + m.setGammaRamp(ramp); + + // technically not needed here / noop because GLFW owns this gamma ramp. + defer ramp.deinit(allocator); + } + } +} diff --git a/pkg/glfw/VideoMode.zig b/pkg/glfw/VideoMode.zig new file mode 100644 index 000000000..f433b8d05 --- /dev/null +++ b/pkg/glfw/VideoMode.zig @@ -0,0 +1,50 @@ +//! Monitor video modes and related functions +//! +//! see also: glfw.Monitor.getVideoMode + +const std = @import("std"); +const c = @import("c.zig").c; + +const VideoMode = @This(); + +handle: c.GLFWvidmode, + +/// Returns the width of the video mode, in screen coordinates. +pub inline fn getWidth(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.width)); +} + +/// Returns the height of the video mode, in screen coordinates. +pub inline fn getHeight(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.height)); +} + +/// Returns the bit depth of the red channel of the video mode. +pub inline fn getRedBits(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.redBits)); +} + +/// Returns the bit depth of the green channel of the video mode. +pub inline fn getGreenBits(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.greenBits)); +} + +/// Returns the bit depth of the blue channel of the video mode. +pub inline fn getBlueBits(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.blueBits)); +} + +/// Returns the refresh rate of the video mode, in Hz. +pub inline fn getRefreshRate(self: VideoMode) u32 { + return @as(u32, @intCast(self.handle.refreshRate)); +} + +test "getters" { + const x = std.mem.zeroes(VideoMode); + _ = x.getWidth(); + _ = x.getHeight(); + _ = x.getRedBits(); + _ = x.getGreenBits(); + _ = x.getBlueBits(); + _ = x.getRefreshRate(); +} diff --git a/pkg/glfw/Window.zig b/pkg/glfw/Window.zig new file mode 100644 index 000000000..29dcac23e --- /dev/null +++ b/pkg/glfw/Window.zig @@ -0,0 +1,3551 @@ +//! Window type and related functions + +const std = @import("std"); +const testing = std.testing; +const mem = std.mem; +const c = @import("c.zig").c; + +const glfw = @import("main.zig"); +const Image = @import("Image.zig"); +const Monitor = @import("Monitor.zig"); +const Cursor = @import("Cursor.zig"); +const Key = @import("key.zig").Key; +const Action = @import("action.zig").Action; +const Mods = @import("mod.zig").Mods; +const MouseButton = @import("mouse_button.zig").MouseButton; + +const internal_debug = @import("internal_debug.zig"); + +const Window = @This(); + +handle: *c.GLFWwindow, + +/// Returns a Zig GLFW window from an underlying C GLFW window handle. +pub inline fn from(handle: *anyopaque) Window { + return Window{ .handle = @as(*c.GLFWwindow, @ptrCast(@alignCast(handle))) }; +} + +/// Resets all window hints to their default values. +/// +/// This function resets all window hints to their default values. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_hints, glfw.Window.hint, glfw.Window.hintString +pub inline fn defaultHints() void { + internal_debug.assertInitialized(); + c.glfwDefaultWindowHints(); +} + +/// Window hints +const Hint = enum(c_int) { + resizable = c.GLFW_RESIZABLE, + visible = c.GLFW_VISIBLE, + decorated = c.GLFW_DECORATED, + focused = c.GLFW_FOCUSED, + auto_iconify = c.GLFW_AUTO_ICONIFY, + floating = c.GLFW_FLOATING, + maximized = c.GLFW_MAXIMIZED, + center_cursor = c.GLFW_CENTER_CURSOR, + transparent_framebuffer = c.GLFW_TRANSPARENT_FRAMEBUFFER, + focus_on_show = c.GLFW_FOCUS_ON_SHOW, + mouse_passthrough = c.GLFW_MOUSE_PASSTHROUGH, + position_x = c.GLFW_POSITION_X, + position_y = c.GLFW_POSITION_Y, + scale_to_monitor = c.GLFW_SCALE_TO_MONITOR, + + /// Framebuffer hints + red_bits = c.GLFW_RED_BITS, + green_bits = c.GLFW_GREEN_BITS, + blue_bits = c.GLFW_BLUE_BITS, + alpha_bits = c.GLFW_ALPHA_BITS, + depth_bits = c.GLFW_DEPTH_BITS, + stencil_bits = c.GLFW_STENCIL_BITS, + accum_red_bits = c.GLFW_ACCUM_RED_BITS, + accum_green_bits = c.GLFW_ACCUM_GREEN_BITS, + accum_blue_bits = c.GLFW_ACCUM_BLUE_BITS, + accum_alpha_bits = c.GLFW_ACCUM_ALPHA_BITS, + aux_buffers = c.GLFW_AUX_BUFFERS, + + /// Framebuffer MSAA samples + samples = c.GLFW_SAMPLES, + + /// Monitor refresh rate + refresh_rate = c.GLFW_REFRESH_RATE, + + /// OpenGL stereoscopic rendering + stereo = c.GLFW_STEREO, + + /// Framebuffer sRGB + srgb_capable = c.GLFW_SRGB_CAPABLE, + + /// Framebuffer double buffering + doublebuffer = c.GLFW_DOUBLEBUFFER, + + client_api = c.GLFW_CLIENT_API, + context_creation_api = c.GLFW_CONTEXT_CREATION_API, + + context_version_major = c.GLFW_CONTEXT_VERSION_MAJOR, + context_version_minor = c.GLFW_CONTEXT_VERSION_MINOR, + + context_robustness = c.GLFW_CONTEXT_ROBUSTNESS, + context_release_behavior = c.GLFW_CONTEXT_RELEASE_BEHAVIOR, + context_no_error = c.GLFW_CONTEXT_NO_ERROR, + // NOTE: This supersedes opengl_debug_context / GLFW_OPENGL_DEBUG_CONTEXT + context_debug = c.GLFW_CONTEXT_DEBUG, + + opengl_forward_compat = c.GLFW_OPENGL_FORWARD_COMPAT, + opengl_profile = c.GLFW_OPENGL_PROFILE, + + /// macOS specific + cocoa_retina_framebuffer = c.GLFW_COCOA_RETINA_FRAMEBUFFER, + + /// macOS specific + cocoa_frame_name = c.GLFW_COCOA_FRAME_NAME, + + /// macOS specific + cocoa_graphics_switching = c.GLFW_COCOA_GRAPHICS_SWITCHING, + + /// X11 specific + x11_class_name = c.GLFW_X11_CLASS_NAME, + + /// X11 specific + x11_instance_name = c.GLFW_X11_INSTANCE_NAME, + + /// Windows specific + win32_keyboard_menu = c.GLFW_WIN32_KEYBOARD_MENU, + + /// Allows specification of the Wayland app_id. + wayland_app_id = c.GLFW_WAYLAND_APP_ID, +}; + +/// Window hints +pub const Hints = struct { + // Note: The defaults here are directly from the GLFW source of the glfwDefaultWindowHints function + resizable: bool = true, + visible: bool = true, + decorated: bool = true, + focused: bool = true, + auto_iconify: bool = true, + floating: bool = false, + maximized: bool = false, + center_cursor: bool = true, + transparent_framebuffer: bool = false, + focus_on_show: bool = true, + mouse_passthrough: bool = false, + position_x: c_int = @intFromEnum(Position.any), + position_y: c_int = @intFromEnum(Position.any), + + scale_to_monitor: bool = false, + + /// Framebuffer hints + red_bits: ?PositiveCInt = 8, + green_bits: ?PositiveCInt = 8, + blue_bits: ?PositiveCInt = 8, + alpha_bits: ?PositiveCInt = 8, + depth_bits: ?PositiveCInt = 24, + stencil_bits: ?PositiveCInt = 8, + accum_red_bits: ?PositiveCInt = 0, + accum_green_bits: ?PositiveCInt = 0, + accum_blue_bits: ?PositiveCInt = 0, + accum_alpha_bits: ?PositiveCInt = 0, + aux_buffers: ?PositiveCInt = 0, + + /// Framebuffer MSAA samples + samples: ?PositiveCInt = 0, + + /// Monitor refresh rate + refresh_rate: ?PositiveCInt = null, + + /// OpenGL stereoscopic rendering + stereo: bool = false, + + /// Framebuffer sRGB + srgb_capable: bool = false, + + /// Framebuffer double buffering + doublebuffer: bool = true, + + client_api: ClientAPI = .opengl_api, + context_creation_api: ContextCreationAPI = .native_context_api, + + context_version_major: c_int = 1, + context_version_minor: c_int = 0, + + context_robustness: ContextRobustness = .no_robustness, + context_release_behavior: ContextReleaseBehavior = .any_release_behavior, + + /// Note: disables the context creating errors, + /// instead turning them into undefined behavior. + context_no_error: bool = false, + context_debug: bool = false, + + opengl_forward_compat: bool = false, + + opengl_profile: OpenGLProfile = .opengl_any_profile, + + /// macOS specific + cocoa_retina_framebuffer: bool = true, + + /// macOS specific + cocoa_frame_name: [:0]const u8 = "", + + /// macOS specific + cocoa_graphics_switching: bool = false, + + /// X11 specific + x11_class_name: [:0]const u8 = "", + + /// X11 specific + x11_instance_name: [:0]const u8 = "", + + /// Windows specific + win32_keyboard_menu: bool = false, + + /// Allows specification of the Wayland app_id. + wayland_app_id: [:0]const u8 = "", + + pub const PositiveCInt = std.math.IntFittingRange(0, std.math.maxInt(c_int)); + + pub const ClientAPI = enum(c_int) { + opengl_api = c.GLFW_OPENGL_API, + opengl_es_api = c.GLFW_OPENGL_ES_API, + no_api = c.GLFW_NO_API, + }; + + pub const ContextCreationAPI = enum(c_int) { + native_context_api = c.GLFW_NATIVE_CONTEXT_API, + egl_context_api = c.GLFW_EGL_CONTEXT_API, + osmesa_context_api = c.GLFW_OSMESA_CONTEXT_API, + }; + + pub const ContextRobustness = enum(c_int) { + no_robustness = c.GLFW_NO_ROBUSTNESS, + no_reset_notification = c.GLFW_NO_RESET_NOTIFICATION, + lose_context_on_reset = c.GLFW_LOSE_CONTEXT_ON_RESET, + }; + + pub const ContextReleaseBehavior = enum(c_int) { + any_release_behavior = c.GLFW_ANY_RELEASE_BEHAVIOR, + release_behavior_flush = c.GLFW_RELEASE_BEHAVIOR_FLUSH, + release_behavior_none = c.GLFW_RELEASE_BEHAVIOR_NONE, + }; + + pub const OpenGLProfile = enum(c_int) { + opengl_any_profile = c.GLFW_OPENGL_ANY_PROFILE, + opengl_compat_profile = c.GLFW_OPENGL_COMPAT_PROFILE, + opengl_core_profile = c.GLFW_OPENGL_CORE_PROFILE, + }; + + pub const Position = enum(c_int) { + /// By default, newly created windows use the placement recommended by the window system, + /// + /// To create the window at a specific position, make it initially invisible using the + /// Window.Hint.visible hint, set its Window.Hint.position and then Window.hide() it. + /// + /// To create the window at a specific position, set the Window.Hint.position_x and + /// Window.Hint.position_y hints before creation. To restore the default behavior, set + /// either or both hints back to Window.Hints.Position.any + any = @bitCast(c.GLFW_ANY_POSITION), + }; + + fn set(hints: Hints) void { + internal_debug.assertInitialized(); + inline for (comptime std.meta.fieldNames(Hint)) |field_name| { + const hint_tag = @intFromEnum(@field(Hint, field_name)); + const hint_value = @field(hints, field_name); + switch (@TypeOf(hint_value)) { + bool => c.glfwWindowHint(hint_tag, @intFromBool(hint_value)), + ?PositiveCInt => c.glfwWindowHint(hint_tag, if (hint_value) |unwrapped| unwrapped else glfw.dont_care), + c_int => c.glfwWindowHint(hint_tag, hint_value), + + ClientAPI, + ContextCreationAPI, + ContextRobustness, + ContextReleaseBehavior, + OpenGLProfile, + Position, + => c.glfwWindowHint(hint_tag, @intFromEnum(hint_value)), + + [:0]const u8 => c.glfwWindowHintString(hint_tag, hint_value.ptr), + + else => unreachable, + } + } + } +}; + +/// Creates a window and its associated context. +/// +/// This function creates a window and its associated OpenGL or OpenGL ES context. Most of the +/// options controlling how the window and its context should be created are specified with window +/// hints using `glfw.Window.hint`. +/// +/// Successful creation does not change which context is current. Before you can use the newly +/// created context, you need to make it current using `glfw.makeContextCurrent`. For +/// information about the `share` parameter, see context_sharing. +/// +/// The created window, framebuffer and context may differ from what you requested, as not all +/// parameters and hints are hard constraints. This includes the size of the window, especially for +/// full screen windows. To query the actual attributes of the created window, framebuffer and +/// context, see glfw.Window.getAttrib, glfw.Window.getSize and glfw.window.getFramebufferSize. +/// +/// To create a full screen window, you need to specify the monitor the window will cover. If no +/// monitor is specified, the window will be windowed mode. Unless you have a way for the user to +/// choose a specific monitor, it is recommended that you pick the primary monitor. For more +/// information on how to query connected monitors, see @ref monitor_monitors. +/// +/// For full screen windows, the specified size becomes the resolution of the window's _desired +/// video mode_. As long as a full screen window is not iconified, the supported video mode most +/// closely matching the desired video mode is set for the specified monitor. For more information +/// about full screen windows, including the creation of so called _windowed full screen_ or +/// _borderless full screen_ windows, see window_windowed_full_screen. +/// +/// Once you have created the window, you can switch it between windowed and full screen mode with +/// glfw.Window.setMonitor. This will not affect its OpenGL or OpenGL ES context. +/// +/// By default, newly created windows use the placement recommended by the window system. To create +/// the window at a specific position, make it initially invisible using the `visible` window +/// hint, set its position and then show it. +/// +/// As long as at least one full screen window is not iconified, the screensaver is prohibited from +/// starting. +/// +/// Window systems put limits on window sizes. Very large or very small window dimensions may be +/// overridden by the window system on creation. Check the actual size after creation. +/// +/// The swap interval is not set during window creation and the initial value may vary depending on +/// driver settings and defaults. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum, glfw.ErrorCode.InvalidValue, +/// glfw.ErrorCode.APIUnavailable, glfw.ErrorCode.VersionUnavailable, glfw.ErrorCode.FormatUnavailable and +/// glfw.ErrorCode.PlatformError. +/// Returns null in the event of an error. +/// +/// Parameters are as follows: +/// +/// * `width` The desired width, in screen coordinates, of the window. +/// * `height` The desired height, in screen coordinates, of the window. +/// * `title` The initial, UTF-8 encoded window title. +/// * `monitor` The monitor to use for full screen mode, or `null` for windowed mode. +/// * `share` The window whose context to share resources with, or `null` to not share resources. +/// +/// win32: Window creation will fail if the Microsoft GDI software OpenGL implementation is the +/// only one available. +/// +/// win32: If the executable has an icon resource named `GLFW_ICON`, it will be set as the initial +/// icon for the window. If no such icon is present, the `IDI_APPLICATION` icon will be used +/// instead. To set a different icon, see glfw.Window.setIcon. +/// +/// win32: The context to share resources with must not be current on any other thread. +/// +/// macos: The OS only supports forward-compatible core profile contexts for OpenGL versions 3.2 +/// and later. Before creating an OpenGL context of version 3.2 or later you must set the +/// `glfw.opengl_forward_compat` and `glfw.opengl_profile` hints accordingly. OpenGL 3.0 and 3.1 +/// contexts are not supported at all on macOS. +/// +/// macos: The OS only supports core profile contexts for OpenGL versions 3.2 and later. Before +/// creating an OpenGL context of version 3.2 or later you must set the `glfw.opengl_profile` hint +/// accordingly. OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. +/// +/// macos: The GLFW window has no icon, as it is not a document window, but the dock icon will be +/// the same as the application bundle's icon. For more information on bundles, see the +/// [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) +/// in the Mac Developer Library. +/// +/// macos: On OS X 10.10 and later the window frame will not be rendered at full resolution on +/// Retina displays unless the glfw.cocoa_retina_framebuffer hint is true (1) and the `NSHighResolutionCapable` +/// key is enabled in the application bundle's `Info.plist`. For more information, see +/// [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) +/// in the Mac Developer Library. The GLFW test and example programs use a custom `Info.plist` +/// template for this, which can be found as `CMake/Info.plist.in` in the source tree. +/// +/// macos: When activating frame autosaving with glfw.cocoa_frame_name, the specified window size +/// and position may be overridden by previously saved values. +/// +/// x11: Some window managers will not respect the placement of initially hidden windows. +/// +/// x11: Due to the asynchronous nature of X11, it may take a moment for a window to reach its +/// requested state. This means you may not be able to query the final size, position or other +/// attributes directly after window creation. +/// +/// x11: The class part of the `WM_CLASS` window property will by default be set to the window title +/// passed to this function. The instance part will use the contents of the `RESOURCE_NAME` +/// environment variable, if present and not empty, or fall back to the window title. Set the glfw.x11_class_name +/// and glfw.x11_instance_name window hints to override this. +/// +/// wayland: Compositors should implement the xdg-decoration protocol for GLFW to decorate the +/// window properly. If this protocol isn't supported, or if the compositor prefers client-side +/// decorations, a very simple fallback frame will be drawn using the wp_viewporter protocol. A +/// compositor can still emit close, maximize or fullscreen events, using for instance a keybind +/// mechanism. If neither of these protocols is supported, the window won't be decorated. +/// +/// wayland: A full screen window will not attempt to change the mode, no matter what the +/// requested size or refresh rate. +/// +/// wayland: Screensaver inhibition requires the idle-inhibit protocol to be implemented in the +/// user's compositor. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_creation, glfw.Window.destroy +pub inline fn create( + width: u32, + height: u32, + title: [*:0]const u8, + monitor: ?Monitor, + share: ?Window, + hints: Hints, +) ?Window { + internal_debug.assertInitialized(); + const ignore_hints_struct = if (comptime @import("builtin").is_test) testing_ignore_window_hints_struct else false; + if (!ignore_hints_struct) hints.set(); + + if (c.glfwCreateWindow( + @as(c_int, @intCast(width)), + @as(c_int, @intCast(height)), + &title[0], + if (monitor) |m| m.handle else null, + if (share) |w| w.handle else null, + )) |handle| return from(handle); + return null; +} + +var testing_ignore_window_hints_struct = if (@import("builtin").is_test) false else @as(void, {}); + +/// Destroys the specified window and its context. +/// +/// This function destroys the specified window and its context. On calling this function, no +/// further callbacks will be called for that window. +/// +/// If the context of the specified window is current on the main thread, it is detached before +/// being destroyed. +/// +/// note: The context of the specified window must not be current on any other thread when this +/// function is called. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @reentrancy This function must not be called from a callback. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_creation, glfw.Window.create +pub inline fn destroy(self: Window) void { + internal_debug.assertInitialized(); + c.glfwDestroyWindow(self.handle); +} + +/// Checks the close flag of the specified window. +/// +/// This function returns the value of the close flag of the specified window. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: window_close +pub inline fn shouldClose(self: Window) bool { + internal_debug.assertInitialized(); + return c.glfwWindowShouldClose(self.handle) == c.GLFW_TRUE; +} + +/// Sets the close flag of the specified window. +/// +/// This function sets the value of the close flag of the specified window. This can be used to +/// override the user's attempt to close the window, or to signal that it should be closed. +/// +/// @thread_safety This function may be called from any thread. Access is not +/// synchronized. +/// +/// see also: window_close +pub inline fn setShouldClose(self: Window, value: bool) void { + internal_debug.assertInitialized(); + const boolean = if (value) c.GLFW_TRUE else c.GLFW_FALSE; + c.glfwSetWindowShouldClose(self.handle, boolean); +} + +/// Sets the UTF-8 encoded title of the specified window. +/// +/// This function sets the window title, encoded as UTF-8, of the specified window. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// macos: The window title will not be updated until the next time you process events. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_title +pub inline fn setTitle(self: Window, title: [*:0]const u8) void { + internal_debug.assertInitialized(); + c.glfwSetWindowTitle(self.handle, title); +} + +/// Sets the icon for the specified window. +/// +/// This function sets the icon of the specified window. If passed an array of candidate images, +/// those of or closest to the sizes desired by the system are selected. If no images are +/// specified, the window reverts to its default icon. +/// +/// The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits per channel with +/// the red channel first. They are arranged canonically as packed sequential rows, starting from +/// the top-left corner. +/// +/// The desired image sizes varies depending on platform and system settings. The selected images +/// will be rescaled as needed. Good sizes include 16x16, 32x32 and 48x48. +/// +/// @pointer_lifetime The specified image data is copied before this function returns. +/// +/// macos: Regular windows do not have icons on macOS. This function will emit FeatureUnavailable. +/// The dock icon will be the same as the application bundle's icon. For more information on +/// bundles, see the [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) +/// in the Mac Developer Library. +/// +/// wayland: There is no existing protocol to change an icon, the window will thus inherit the one +/// defined in the application's desktop file. This function will emit glfw.ErrorCode.FeatureUnavailable. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue, glfw.ErrorCode.FeatureUnavailable +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_icon +pub inline fn setIcon(self: Window, allocator: mem.Allocator, images: ?[]const Image) mem.Allocator.Error!void { + internal_debug.assertInitialized(); + if (images) |im| { + const tmp = try allocator.alloc(c.GLFWimage, im.len); + defer allocator.free(tmp); + for (im, 0..) |img, index| tmp[index] = img.toC(); + c.glfwSetWindowIcon(self.handle, @as(c_int, @intCast(im.len)), &tmp[0]); + } else c.glfwSetWindowIcon(self.handle, 0, null); +} + +pub const Pos = struct { + x: i64, + y: i64, +}; + +/// Retrieves the position of the content area of the specified window. +/// +/// This function retrieves the position, in screen coordinates, of the upper-left corner of the +/// content area of the specified window. +/// +/// Possible errors include glfw.ErrorCode.FeatureUnavailable. +/// Additionally returns a zero value in the event of an error. +/// +/// wayland: There is no way for an application to retrieve the global position of its windows, +/// this function will always emit glfw.ErrorCode.FeatureUnavailable. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_pos glfw.Window.setPos +pub inline fn getPos(self: Window) Pos { + internal_debug.assertInitialized(); + var x: c_int = 0; + var y: c_int = 0; + c.glfwGetWindowPos(self.handle, &x, &y); + return Pos{ .x = @as(i64, @intCast(x)), .y = @as(i64, @intCast(y)) }; +} + +/// Sets the position of the content area of the specified window. +/// +/// This function sets the position, in screen coordinates, of the upper-left corner of the content +/// area of the specified windowed mode window. If the window is a full screen window, this +/// function does nothing. +/// +/// __Do not use this function__ to move an already visible window unless you have very good +/// reasons for doing so, as it will confuse and annoy the user. +/// +/// The window manager may put limits on what positions are allowed. GLFW cannot and should not +/// override these limits. +/// +/// Possible errors include glfw.ErrorCode.FeatureUnavailable. +/// +/// wayland: There is no way for an application to set the global position of its windows, this +/// function will always emit glfw.ErrorCode.FeatureUnavailable. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_pos, glfw.Window.getPos +pub inline fn setPos(self: Window, pos: Pos) void { + internal_debug.assertInitialized(); + c.glfwSetWindowPos(self.handle, @as(c_int, @intCast(pos.x)), @as(c_int, @intCast(pos.y))); +} + +pub const Size = struct { + width: u32, + height: u32, +}; + +/// Retrieves the size of the content area of the specified window. +/// +/// This function retrieves the size, in screen coordinates, of the content area of the specified +/// window. If you wish to retrieve the size of the framebuffer of the window in pixels, see +/// glfw.Window.getFramebufferSize. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_size, glfw.Window.setSize +pub inline fn getSize(self: Window) Size { + internal_debug.assertInitialized(); + var width: c_int = 0; + var height: c_int = 0; + c.glfwGetWindowSize(self.handle, &width, &height); + return Size{ .width = @as(u32, @intCast(width)), .height = @as(u32, @intCast(height)) }; +} + +/// Sets the size of the content area of the specified window. +/// +/// This function sets the size, in screen coordinates, of the content area of the specified window. +/// +/// For full screen windows, this function updates the resolution of its desired video mode and +/// switches to the video mode closest to it, without affecting the window's context. As the +/// context is unaffected, the bit depths of the framebuffer remain unchanged. +/// +/// If you wish to update the refresh rate of the desired video mode in addition to its resolution, +/// see glfw.Window.setMonitor. +/// +/// The window manager may put limits on what sizes are allowed. GLFW cannot and should not +/// override these limits. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// wayland: A full screen window will not attempt to change the mode, no matter what the requested +/// size. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_size, glfw.Window.getSize, glfw.Window.SetMonitor +pub inline fn setSize(self: Window, size: Size) void { + internal_debug.assertInitialized(); + c.glfwSetWindowSize(self.handle, @as(c_int, @intCast(size.width)), @as(c_int, @intCast(size.height))); +} + +/// A size with option width/height, used to represent e.g. constraints on a windows size while +/// allowing specific axis to be unconstrained (null) if desired. +pub const SizeOptional = struct { + width: ?u32 = null, + height: ?u32 = null, +}; + +/// Sets the size limits of the specified window's content area. +/// +/// This function sets the size limits of the content area of the specified window. If the window +/// is full screen, the size limits only take effect/ once it is made windowed. If the window is not +/// resizable, this function does nothing. +/// +/// The size limits are applied immediately to a windowed mode window and may cause it to be resized. +/// +/// The maximum dimensions must be greater than or equal to the minimum dimensions. glfw.dont_care +/// may be used for any width/height parameter. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue and glfw.ErrorCode.PlatformError. +/// +/// If you set size limits and an aspect ratio that conflict, the results are undefined. +/// +/// wayland: The size limits will not be applied until the window is actually resized, either by +/// the user or by the compositor. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_sizelimits, glfw.Window.setAspectRatio +pub inline fn setSizeLimits(self: Window, min: SizeOptional, max: SizeOptional) void { + internal_debug.assertInitialized(); + + if (min.width != null and max.width != null) { + std.debug.assert(min.width.? <= max.width.?); + } + if (min.height != null and max.height != null) { + std.debug.assert(min.height.? <= max.height.?); + } + + c.glfwSetWindowSizeLimits( + self.handle, + if (min.width) |min_width| @as(c_int, @intCast(min_width)) else glfw.dont_care, + if (min.height) |min_height| @as(c_int, @intCast(min_height)) else glfw.dont_care, + if (max.width) |max_width| @as(c_int, @intCast(max_width)) else glfw.dont_care, + if (max.height) |max_height| @as(c_int, @intCast(max_height)) else glfw.dont_care, + ); +} + +/// Sets the aspect ratio of the specified window. +/// +/// This function sets the required aspect ratio of the content area of the specified window. If +/// the window is full screen, the aspect ratio only takes effect once it is made windowed. If the +/// window is not resizable, this function does nothing. +/// +/// The aspect ratio is specified as a numerator and a denominator and both values must be greater +/// than zero. For example, the common 16:9 aspect ratio is specified as 16 and 9, respectively. +/// +/// If the numerator AND denominator is set to `glfw.dont_care` then the aspect ratio limit is +/// disabled. +/// +/// The aspect ratio is applied immediately to a windowed mode window and may cause it to be +/// resized. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue and glfw.ErrorCode.PlatformError. +/// +/// If you set size limits and an aspect ratio that conflict, the results are undefined. +/// +/// wayland: The aspect ratio will not be applied until the window is actually resized, either by +/// the user or by the compositor. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_sizelimits, glfw.Window.setSizeLimits +/// +/// WARNING: on wayland it will return glfw.ErrorCode.FeatureUnimplemented +pub inline fn setAspectRatio(self: Window, numerator: ?u32, denominator: ?u32) void { + internal_debug.assertInitialized(); + + if (numerator != null and denominator != null) { + std.debug.assert(numerator.? > 0); + std.debug.assert(denominator.? > 0); + } + + c.glfwSetWindowAspectRatio( + self.handle, + if (numerator) |numerator_unwrapped| @as(c_int, @intCast(numerator_unwrapped)) else glfw.dont_care, + if (denominator) |denominator_unwrapped| @as(c_int, @intCast(denominator_unwrapped)) else glfw.dont_care, + ); +} + +/// Retrieves the size of the framebuffer of the specified window. +/// +/// This function retrieves the size, in pixels, of the framebuffer of the specified window. If you +/// wish to retrieve the size of the window in screen coordinates, see @ref glfwGetWindowSize. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_fbsize, glfwWindow.setFramebufferSizeCallback +pub inline fn getFramebufferSize(self: Window) Size { + internal_debug.assertInitialized(); + var width: c_int = 0; + var height: c_int = 0; + c.glfwGetFramebufferSize(self.handle, &width, &height); + return Size{ .width = @as(u32, @intCast(width)), .height = @as(u32, @intCast(height)) }; +} + +pub const FrameSize = struct { + left: u32, + top: u32, + right: u32, + bottom: u32, +}; + +/// Retrieves the size of the frame of the window. +/// +/// This function retrieves the size, in screen coordinates, of each edge of the frame of the +/// specified window. This size includes the title bar, if the window has one. The size of the +/// frame may vary depending on the window-related hints used to create it. +/// +/// Because this function retrieves the size of each window frame edge and not the offset along a +/// particular coordinate axis, the retrieved values will always be zero or positive. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_size +pub inline fn getFrameSize(self: Window) FrameSize { + internal_debug.assertInitialized(); + var left: c_int = 0; + var top: c_int = 0; + var right: c_int = 0; + var bottom: c_int = 0; + c.glfwGetWindowFrameSize(self.handle, &left, &top, &right, &bottom); + return FrameSize{ + .left = @as(u32, @intCast(left)), + .top = @as(u32, @intCast(top)), + .right = @as(u32, @intCast(right)), + .bottom = @as(u32, @intCast(bottom)), + }; +} + +pub const ContentScale = struct { + x_scale: f32, + y_scale: f32, +}; + +/// Retrieves the content scale for the specified window. +/// +/// This function retrieves the content scale for the specified window. The content scale is the +/// ratio between the current DPI and the platform's default DPI. This is especially important for +/// text and any UI elements. If the pixel dimensions of your UI scaled by this look appropriate on +/// your machine then it should appear at a reasonable size on other machines regardless of their +/// DPI and scaling settings. This relies on the system DPI and scaling settings being somewhat +/// correct. +/// +/// On platforms where each monitors can have its own content scale, the window content scale will +/// depend on which monitor the system considers the window to be on. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_scale, glfwSetWindowContentScaleCallback, glfwGetMonitorContentScale +pub inline fn getContentScale(self: Window) ContentScale { + internal_debug.assertInitialized(); + var x_scale: f32 = 0; + var y_scale: f32 = 0; + c.glfwGetWindowContentScale(self.handle, &x_scale, &y_scale); + return ContentScale{ .x_scale = x_scale, .y_scale = y_scale }; +} + +/// Returns the opacity of the whole window. +/// +/// This function returns the opacity of the window, including any decorations. +/// +/// The opacity (or alpha) value is a positive finite number between zero and one, where zero is +/// fully transparent and one is fully opaque. If the system does not support whole window +/// transparency, this function always returns one. +/// +/// The initial opacity value for newly created windows is one. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_transparency, glfw.Window.setOpacity +pub inline fn getOpacity(self: Window) f32 { + internal_debug.assertInitialized(); + const opacity = c.glfwGetWindowOpacity(self.handle); + return opacity; +} + +/// Sets the opacity of the whole window. +/// +/// This function sets the opacity of the window, including any decorations. +/// +/// The opacity (or alpha) value is a positive finite number between zero and one, where zero is +/// fully transparent and one is fully opaque. +/// +/// The initial opacity value for newly created windows is one. +/// +/// A window created with framebuffer transparency may not use whole window transparency. The +/// results of doing this are undefined. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_transparency, glfw.Window.getOpacity +pub inline fn setOpacity(self: Window, opacity: f32) void { + internal_debug.assertInitialized(); + c.glfwSetWindowOpacity(self.handle, opacity); +} + +/// Iconifies the specified window. +/// +/// This function iconifies (minimizes) the specified window if it was previously restored. If the +/// window is already iconified, this function does nothing. +/// +/// If the specified window is a full screen window, GLFW restores the original video mode of the +/// monitor. The window's desired video mode is set again when the window is restored. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// wayland: Once a window is iconified, glfw.Window.restorebe able to restore it. This is a design +/// decision of the xdg-shell protocol. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_iconify, glfw.Window.restore, glfw.Window.maximize +pub inline fn iconify(self: Window) void { + internal_debug.assertInitialized(); + c.glfwIconifyWindow(self.handle); +} + +/// Restores the specified window. +/// +/// This function restores the specified window if it was previously iconified (minimized) or +/// maximized. If the window is already restored, this function does nothing. +/// +/// If the specified window is an iconified full screen window, its desired video mode is set +/// again for its monitor when the window is restored. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_iconify, glfw.Window.iconify, glfw.Window.maximize +pub inline fn restore(self: Window) void { + internal_debug.assertInitialized(); + c.glfwRestoreWindow(self.handle); +} + +/// Maximizes the specified window. +/// +/// This function maximizes the specified window if it was previously not maximized. If the window +/// is already maximized, this function does nothing. +/// +/// If the specified window is a full screen window, this function does nothing. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_iconify, glfw.Window.iconify, glfw.Window.restore +pub inline fn maximize(self: Window) void { + internal_debug.assertInitialized(); + c.glfwMaximizeWindow(self.handle); +} + +/// Makes the specified window visible. +/// +/// This function makes the specified window visible if it was previously hidden. If the window is +/// already visible or is in full screen mode, this function does nothing. +/// +/// By default, windowed mode windows are focused when shown Set the glfw.focus_on_show window hint +/// to change this behavior for all newly created windows, or change the +/// behavior for an existing window with glfw.Window.setAttrib. +/// +/// wayland: Because Wayland wants every frame of the desktop to be complete, this function does +/// not immediately make the window visible. Instead it will become visible the next time the window +/// framebuffer is updated after this call. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_hide, glfw.Window.hide +/// +/// WARNING: on wayland it will return glfw.ErrorCode.FeatureUnavailable +pub inline fn show(self: Window) void { + internal_debug.assertInitialized(); + c.glfwShowWindow(self.handle); +} + +/// Hides the specified window. +/// +/// This function hides the specified window if it was previously visible. If the window is already +/// hidden or is in full screen mode, this function does nothing. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_hide, glfw.Window.show +pub inline fn hide(self: Window) void { + internal_debug.assertInitialized(); + c.glfwHideWindow(self.handle); +} + +/// Brings the specified window to front and sets input focus. +/// +/// This function brings the specified window to front and sets input focus. The window should +/// already be visible and not iconified. +/// +/// By default, both windowed and full screen mode windows are focused when initially created. Set +/// the glfw.focused to disable this behavior. +/// +/// Also by default, windowed mode windows are focused when shown with glfw.Window.show. Set the +/// glfw.focus_on_show to disable this behavior. +/// +/// __Do not use this function__ to steal focus from other applications unless you are certain that +/// is what the user wants. Focus stealing can be extremely disruptive. +/// +/// For a less disruptive way of getting the user's attention, see [attention requests (window_attention). +/// +/// wayland It is not possible for an application to set the input focus. This function will emit +/// glfw.ErrorCode.FeatureUnavailable. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_focus, window_attention +pub inline fn focus(self: Window) void { + internal_debug.assertInitialized(); + c.glfwFocusWindow(self.handle); +} + +/// Requests user attention to the specified window. +/// +/// This function requests user attention to the specified window. On platforms where this is not +/// supported, attention is requested to the application as a whole. +/// +/// Once the user has given attention, usually by focusing the window or application, the system will end the request automatically. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// macos: Attention is requested to the application as a whole, not the +/// specific window. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_attention +/// +/// WARNING: on wayland it will return glfw.ErrorCode.FeatureUnimplemented +pub inline fn requestAttention(self: Window) void { + internal_debug.assertInitialized(); + c.glfwRequestWindowAttention(self.handle); +} + +/// Swaps the front and back buffers of the specified window. +/// +/// This function swaps the front and back buffers of the specified window when rendering with +/// OpenGL or OpenGL ES. If the swap interval is greater than zero, the GPU driver waits the +/// specified number of screen updates before swapping the buffers. +/// +/// The specified window must have an OpenGL or OpenGL ES context. Specifying a window without a +/// context will generate glfw.ErrorCode.NoWindowContext. +/// +/// This function does not apply to Vulkan. If you are rendering with Vulkan, see `vkQueuePresentKHR` +/// instead. +/// +/// @param[in] window The window whose buffers to swap. +/// +/// Possible errors include glfw.ErrorCode.NoWindowContext and glfw.ErrorCode.PlatformError. +/// +/// __EGL:__ The context of the specified window must be current on the calling thread. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: buffer_swap, glfwSwapInterval +pub inline fn swapBuffers(self: Window) void { + internal_debug.assertInitialized(); + c.glfwSwapBuffers(self.handle); +} + +/// Returns the monitor that the window uses for full screen mode. +/// +/// This function returns the handle of the monitor that the specified window is in full screen on. +/// +/// @return The monitor, or null if the window is in windowed mode. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_monitor, glfw.Window.setMonitor +pub inline fn getMonitor(self: Window) ?Monitor { + internal_debug.assertInitialized(); + if (c.glfwGetWindowMonitor(self.handle)) |monitor| return Monitor{ .handle = monitor }; + return null; +} + +/// Sets the mode, monitor, video mode and placement of a window. +/// +/// This function sets the monitor that the window uses for full screen mode or, if the monitor is +/// null, makes it windowed mode. +/// +/// When setting a monitor, this function updates the width, height and refresh rate of the desired +/// video mode and switches to the video mode closest to it. The window position is ignored when +/// setting a monitor. +/// +/// When the monitor is null, the position, width and height are used to place the window content +/// area. The refresh rate is ignored when no monitor is specified. +/// +/// If you only wish to update the resolution of a full screen window or the size of a windowed +/// mode window, see @ref glfwSetWindowSize. +/// +/// When a window transitions from full screen to windowed mode, this function restores any +/// previous window settings such as whether it is decorated, floating, resizable, has size or +/// aspect ratio limits, etc. +/// +/// @param[in] window The window whose monitor, size or video mode to set. +/// @param[in] monitor The desired monitor, or null to set windowed mode. +/// @param[in] xpos The desired x-coordinate of the upper-left corner of the content area. +/// @param[in] ypos The desired y-coordinate of the upper-left corner of the content area. +/// @param[in] width The desired with, in screen coordinates, of the content area or video mode. +/// @param[in] height The desired height, in screen coordinates, of the content area or video mode. +/// @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, or `glfw.dont_care`. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// The OpenGL or OpenGL ES context will not be destroyed or otherwise affected by any resizing or +/// mode switching, although you may need to update your viewport if the framebuffer size has +/// changed. +/// +/// wayland: The desired window position is ignored, as there is no way for an application to set +/// this property. +/// +/// wayland: Setting the window to full screen will not attempt to change the mode, no matter what +/// the requested size or refresh rate. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_monitor, window_full_screen, glfw.Window.getMonitor, glfw.Window.setSize +pub inline fn setMonitor(self: Window, monitor: ?Monitor, xpos: i32, ypos: i32, width: u32, height: u32, refresh_rate: ?u32) void { + internal_debug.assertInitialized(); + c.glfwSetWindowMonitor( + self.handle, + if (monitor) |m| m.handle else null, + @as(c_int, @intCast(xpos)), + @as(c_int, @intCast(ypos)), + @as(c_int, @intCast(width)), + @as(c_int, @intCast(height)), + if (refresh_rate) |refresh_rate_unwrapped| @as(c_int, @intCast(refresh_rate_unwrapped)) else glfw.dont_care, + ); +} + +/// Window attributes +pub const Attrib = enum(c_int) { + iconified = c.GLFW_ICONIFIED, + resizable = c.GLFW_RESIZABLE, + visible = c.GLFW_VISIBLE, + decorated = c.GLFW_DECORATED, + focused = c.GLFW_FOCUSED, + auto_iconify = c.GLFW_AUTO_ICONIFY, + floating = c.GLFW_FLOATING, + maximized = c.GLFW_MAXIMIZED, + transparent_framebuffer = c.GLFW_TRANSPARENT_FRAMEBUFFER, + hovered = c.GLFW_HOVERED, + focus_on_show = c.GLFW_FOCUS_ON_SHOW, + mouse_passthrough = c.GLFW_MOUSE_PASSTHROUGH, + doublebuffer = c.GLFW_DOUBLEBUFFER, + + client_api = c.GLFW_CLIENT_API, + context_creation_api = c.GLFW_CONTEXT_CREATION_API, + context_version_major = c.GLFW_CONTEXT_VERSION_MAJOR, + context_version_minor = c.GLFW_CONTEXT_VERSION_MINOR, + context_revision = c.GLFW_CONTEXT_REVISION, + + context_robustness = c.GLFW_CONTEXT_ROBUSTNESS, + context_release_behavior = c.GLFW_CONTEXT_RELEASE_BEHAVIOR, + context_no_error = c.GLFW_CONTEXT_NO_ERROR, + context_debug = c.GLFW_CONTEXT_DEBUG, + + opengl_forward_compat = c.GLFW_OPENGL_FORWARD_COMPAT, + opengl_profile = c.GLFW_OPENGL_PROFILE, +}; + +/// Returns an attribute of the specified window. +/// +/// This function returns the value of an attribute of the specified window or its OpenGL or OpenGL +/// ES context. +/// +/// @param[in] attrib The window attribute (see window_attribs) whose value to return. +/// @return The value of the attribute, or zero if an error occurred. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// Framebuffer related hints are not window attributes. See window_attribs_fb for more information. +/// +/// Zero is a valid value for many window and context related attributes so you cannot use a return +/// value of zero as an indication of errors. However, this function should not fail as long as it +/// is passed valid arguments and the library has been initialized. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// wayland: The Wayland protocol provides no way to check whether a window is iconified, so +/// glfw.Window.Attrib.iconified always returns `false`. +/// +/// see also: window_attribs, glfw.Window.setAttrib +pub inline fn getAttrib(self: Window, attrib: Attrib) i32 { + internal_debug.assertInitialized(); + return c.glfwGetWindowAttrib(self.handle, @intFromEnum(attrib)); +} + +/// Sets an attribute of the specified window. +/// +/// This function sets the value of an attribute of the specified window. +/// +/// The supported attributes are glfw.decorated, glfw.resizable, glfw.floating, glfw.auto_iconify, +/// glfw.focus_on_show. +/// +/// Some of these attributes are ignored for full screen windows. The new value will take effect +/// if the window is later made windowed. +/// +/// Some of these attributes are ignored for windowed mode windows. The new value will take effect +/// if the window is later made full screen. +/// +/// @param[in] attrib A supported window attribute. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum, glfw.ErrorCode.InvalidValue, +/// glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable +/// +/// Calling glfw.Window.getAttrib will always return the latest +/// value, even if that value is ignored by the current mode of the window. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_attribs, glfw.Window.getAttrib +/// +pub inline fn setAttrib(self: Window, attrib: Attrib, value: bool) void { + internal_debug.assertInitialized(); + std.debug.assert(switch (attrib) { + .decorated, + .resizable, + .floating, + .auto_iconify, + .focus_on_show, + .mouse_passthrough, + .doublebuffer, + => true, + else => false, + }); + c.glfwSetWindowAttrib(self.handle, @intFromEnum(attrib), if (value) c.GLFW_TRUE else c.GLFW_FALSE); +} + +/// Sets the user pointer of the specified window. +/// +/// This function sets the user-defined pointer of the specified window. The current value is +/// retained until the window is destroyed. The initial value is null. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: window_userptr, glfw.Window.getUserPointer +pub inline fn setUserPointer(self: Window, pointer: ?*anyopaque) void { + internal_debug.assertInitialized(); + c.glfwSetWindowUserPointer(self.handle, pointer); +} + +/// Returns the user pointer of the specified window. +/// +/// This function returns the current value of the user-defined pointer of the specified window. +/// The initial value is null. +/// +/// @thread_safety This function may be called from any thread. Access is not synchronized. +/// +/// see also: window_userptr, glfw.Window.setUserPointer +pub inline fn getUserPointer(self: Window, comptime T: type) ?*T { + internal_debug.assertInitialized(); + if (c.glfwGetWindowUserPointer(self.handle)) |user_pointer| return @as(?*T, @ptrCast(@alignCast(user_pointer))); + return null; +} + +/// Sets the position callback for the specified window. +/// +/// This function sets the position callback of the specified window, which is called when the +/// window is moved. The callback is provided with the position, in screen coordinates, of the +/// upper-left corner of the content area of the window. +/// +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param `window` the window that moved. +/// @callback_param `xpos` the new x-coordinate, in screen coordinates, of the upper-left corner of +/// the content area of the window. +/// @callback_param `ypos` the new y-coordinate, in screen coordinates, of the upper-left corner of +/// the content area of the window. +/// +/// wayland: This callback will never be called, as there is no way for an application to know its +/// global position. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_pos +pub inline fn setPosCallback(self: Window, comptime callback: ?fn (window: Window, xpos: i32, ypos: i32) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn posCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(i32, @intCast(xpos)), + @as(i32, @intCast(ypos)), + }); + } + }; + + if (c.glfwSetWindowPosCallback(self.handle, CWrapper.posCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowPosCallback(self.handle, null) != null) return; + } +} + +/// Sets the size callback for the specified window. +/// +/// This function sets the size callback of the specified window, which is called when the window +/// is resized. The callback is provided with the size, in screen coordinates, of the content area +/// of the window. +/// +/// @callback_param `window` the window that was resized. +/// @callback_param `width` the new width, in screen coordinates, of the window. +/// @callback_param `height` the new height, in screen coordinates, of the window. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_size +pub inline fn setSizeCallback(self: Window, comptime callback: ?fn (window: Window, width: i32, height: i32) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn sizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(i32, @intCast(width)), + @as(i32, @intCast(height)), + }); + } + }; + + if (c.glfwSetWindowSizeCallback(self.handle, CWrapper.sizeCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowSizeCallback(self.handle, null) != null) return; + } +} + +/// Sets the close callback for the specified window. +/// +/// This function sets the close callback of the specified window, which is called when the user +/// attempts to close the window, for example by clicking the close widget in the title bar. +/// +/// The close flag is set before this callback is called, but you can modify it at any time with +/// glfw.Window.setShouldClose. +/// +/// The close callback is not triggered by glfw.Window.destroy. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param `window` the window that the user attempted to close. +/// +/// macos: Selecting Quit from the application menu will trigger the close callback for all +/// windows. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_close +pub inline fn setCloseCallback(self: Window, comptime callback: ?fn (window: Window) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn closeCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + }); + } + }; + + if (c.glfwSetWindowCloseCallback(self.handle, CWrapper.closeCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowCloseCallback(self.handle, null) != null) return; + } +} + +/// Sets the refresh callback for the specified window. +/// +/// This function sets the refresh callback of the specified window, which is +/// called when the content area of the window needs to be redrawn, for example +/// if the window has been exposed after having been covered by another window. +/// +/// On compositing window systems such as Aero, Compiz, Aqua or Wayland, where +/// the window contents are saved off-screen, this callback may be called only +/// very infrequently or never at all. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window whose content needs to be refreshed. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_refresh +pub inline fn setRefreshCallback(self: Window, comptime callback: ?fn (window: Window) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn refreshCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + }); + } + }; + + if (c.glfwSetWindowRefreshCallback(self.handle, CWrapper.refreshCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowRefreshCallback(self.handle, null) != null) return; + } +} + +/// Sets the focus callback for the specified window. +/// +/// This function sets the focus callback of the specified window, which is +/// called when the window gains or loses input focus. +/// +/// After the focus callback is called for a window that lost input focus, +/// synthetic key and mouse button release events will be generated for all such +/// that had been pressed. For more information, see @ref glfwSetKeyCallback +/// and @ref glfwSetMouseButtonCallback. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window whose input focus has changed. +/// @callback_param `focused` `true` if the window was given input focus, or `false` if it lost it. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_focus +pub inline fn setFocusCallback(self: Window, comptime callback: ?fn (window: Window, focused: bool) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn focusCallbackWrapper(handle: ?*c.GLFWwindow, focused: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + focused == c.GLFW_TRUE, + }); + } + }; + + if (c.glfwSetWindowFocusCallback(self.handle, CWrapper.focusCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowFocusCallback(self.handle, null) != null) return; + } +} + +/// Sets the iconify callback for the specified window. +/// +/// This function sets the iconification callback of the specified window, which +/// is called when the window is iconified or restored. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window which was iconified or restored. +/// @callback_param `iconified` `true` if the window was iconified, or `false` if it was restored. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_iconify +pub inline fn setIconifyCallback(self: Window, comptime callback: ?fn (window: Window, iconified: bool) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn iconifyCallbackWrapper(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + iconified == c.GLFW_TRUE, + }); + } + }; + + if (c.glfwSetWindowIconifyCallback(self.handle, CWrapper.iconifyCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowIconifyCallback(self.handle, null) != null) return; + } +} + +/// Sets the maximize callback for the specified window. +/// +/// This function sets the maximization callback of the specified window, which +/// is called when the window is maximized or restored. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window which was maximized or restored. +/// @callback_param `maximized` `true` if the window was maximized, or `false` if it was restored. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_maximize +// GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); +pub inline fn setMaximizeCallback(self: Window, comptime callback: ?fn (window: Window, maximized: bool) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn maximizeCallbackWrapper(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + maximized == c.GLFW_TRUE, + }); + } + }; + + if (c.glfwSetWindowMaximizeCallback(self.handle, CWrapper.maximizeCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowMaximizeCallback(self.handle, null) != null) return; + } +} + +/// Sets the framebuffer resize callback for the specified window. +/// +/// This function sets the framebuffer resize callback of the specified window, +/// which is called when the framebuffer of the specified window is resized. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window whose framebuffer was resized. +/// @callback_param `width` the new width, in pixels, of the framebuffer. +/// @callback_param `height` the new height, in pixels, of the framebuffer. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_fbsize +pub inline fn setFramebufferSizeCallback(self: Window, comptime callback: ?fn (window: Window, width: u32, height: u32) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn framebufferSizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(u32, @intCast(width)), + @as(u32, @intCast(height)), + }); + } + }; + + if (c.glfwSetFramebufferSizeCallback(self.handle, CWrapper.framebufferSizeCallbackWrapper) != null) return; + } else { + if (c.glfwSetFramebufferSizeCallback(self.handle, null) != null) return; + } +} + +/// Sets the window content scale callback for the specified window. +/// +/// This function sets the window content scale callback of the specified window, +/// which is called when the content scale of the specified window changes. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set +/// callback. +/// +/// @callback_param `window` the window whose content scale changed. +/// @callback_param `xscale` the new x-axis content scale of the window. +/// @callback_param `yscale` the new y-axis content scale of the window. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_scale, glfw.Window.getContentScale +pub inline fn setContentScaleCallback(self: Window, comptime callback: ?fn (window: Window, xscale: f32, yscale: f32) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn windowScaleCallbackWrapper(handle: ?*c.GLFWwindow, xscale: f32, yscale: f32) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + xscale, + yscale, + }); + } + }; + + if (c.glfwSetWindowContentScaleCallback(self.handle, CWrapper.windowScaleCallbackWrapper) != null) return; + } else { + if (c.glfwSetWindowContentScaleCallback(self.handle, null) != null) return; + } +} + +pub const InputMode = enum(c_int) { + cursor = c.GLFW_CURSOR, + sticky_keys = c.GLFW_STICKY_KEYS, + sticky_mouse_buttons = c.GLFW_STICKY_MOUSE_BUTTONS, + lock_key_mods = c.GLFW_LOCK_KEY_MODS, + raw_mouse_motion = c.GLFW_RAW_MOUSE_MOTION, +}; + +/// A cursor input mode to be supplied to `glfw.Window.setInputModeCursor` +pub const InputModeCursor = enum(c_int) { + /// Makes the cursor visible and behaving normally. + normal = c.GLFW_CURSOR_NORMAL, + + /// Makes the cursor invisible when it is over the content area of the window but does not + /// restrict it from leaving. + hidden = c.GLFW_CURSOR_HIDDEN, + + /// Hides and grabs the cursor, providing virtual and unlimited cursor movement. This is useful + /// for implementing for example 3D camera controls. + disabled = c.GLFW_CURSOR_DISABLED, + + /// Makes the cursor visible but confines it to the content area of the window. + captured = c.GLFW_CURSOR_CAPTURED, +}; + +/// Sets the input mode of the cursor, whether it should behave normally, be hidden, or grabbed. +pub inline fn setInputModeCursor(self: Window, value: InputModeCursor) void { + return self.setInputMode(InputMode.cursor, value); +} + +/// Gets the current input mode of the cursor. +pub inline fn getInputModeCursor(self: Window) InputModeCursor { + return @as(InputModeCursor, @enumFromInt(self.getInputMode(InputMode.cursor))); +} + +/// Sets the input mode of sticky keys, if enabled a key press will ensure that `glfw.Window.getKey` +/// return `.press` the next time it is called even if the key had been released before the call. +/// +/// This is useful when you are only interested in whether keys have been pressed but not when or +/// in which order. +pub inline fn setInputModeStickyKeys(self: Window, enabled: bool) void { + return self.setInputMode(InputMode.sticky_keys, enabled); +} + +/// Tells if the sticky keys input mode is enabled. +pub inline fn getInputModeStickyKeys(self: Window) bool { + return self.getInputMode(InputMode.sticky_keys) == 1; +} + +/// Sets the input mode of sticky mouse buttons, if enabled a mouse button press will ensure that +/// `glfw.Window.getMouseButton` return `.press` the next time it is called even if the button had +/// been released before the call. +/// +/// This is useful when you are only interested in whether buttons have been pressed but not when +/// or in which order. +pub inline fn setInputModeStickyMouseButtons(self: Window, enabled: bool) void { + return self.setInputMode(InputMode.sticky_mouse_buttons, enabled); +} + +/// Tells if the sticky mouse buttons input mode is enabled. +pub inline fn getInputModeStickyMouseButtons(self: Window) bool { + return self.getInputMode(InputMode.sticky_mouse_buttons) == 1; +} + +/// Sets the input mode of locking key modifiers, if enabled callbacks that receive modifier bits +/// will also have the glfw.mod.caps_lock bit set when the event was generated with Caps Lock on, +/// and the glfw.mod.num_lock bit when Num Lock was on. +pub inline fn setInputModeLockKeyMods(self: Window, enabled: bool) void { + return self.setInputMode(InputMode.lock_key_mods, enabled); +} + +/// Tells if the locking key modifiers input mode is enabled. +pub inline fn getInputModeLockKeyMods(self: Window) bool { + return self.getInputMode(InputMode.lock_key_mods) == 1; +} + +/// Sets whether the raw mouse motion input mode is enabled, if enabled unscaled and unaccelerated +/// mouse motion events will be sent, otherwise standard mouse motion events respecting the user's +/// OS settings will be sent. +/// +/// If raw motion is not supported, attempting to set this will emit glfw.ErrorCode.FeatureUnavailable. +/// Call glfw.rawMouseMotionSupported to check for support. +pub inline fn setInputModeRawMouseMotion(self: Window, enabled: bool) void { + return self.setInputMode(InputMode.raw_mouse_motion, enabled); +} + +/// Tells if the raw mouse motion input mode is enabled. +pub inline fn getInputModeRawMouseMotion(self: Window) bool { + return self.getInputMode(InputMode.raw_mouse_motion) == 1; +} + +/// Returns the value of an input option for the specified window. +/// +/// Consider using one of the following variants instead, if applicable, as they'll give you a +/// typed return value: +/// +/// * `glfw.Window.getInputModeCursor` +/// * `glfw.Window.getInputModeStickyKeys` +/// * `glfw.Window.getInputModeStickyMouseButtons` +/// * `glfw.Window.getInputModeLockKeyMods` +/// * `glfw.Window.getInputModeRawMouseMotion` +/// +/// This function returns the value of an input option for the specified window. The mode must be +/// one of the `glfw.Window.InputMode` enumerations. +/// +/// Boolean values, such as for `glfw.Window.InputMode.raw_mouse_motion`, are returned as integers. +/// You may convert to a boolean using `== 1`. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: glfw.Window.setInputMode +pub inline fn getInputMode(self: Window, mode: InputMode) i32 { + internal_debug.assertInitialized(); + const value = c.glfwGetInputMode(self.handle, @intFromEnum(mode)); + return @as(i32, @intCast(value)); +} + +/// Sets an input option for the specified window. +/// +/// Consider using one of the following variants instead, if applicable, as they'll guide you to +/// the right input value via enumerations: +/// +/// * `glfw.Window.setInputModeCursor` +/// * `glfw.Window.setInputModeStickyKeys` +/// * `glfw.Window.setInputModeStickyMouseButtons` +/// * `glfw.Window.setInputModeLockKeyMods` +/// * `glfw.Window.setInputModeRawMouseMotion` +/// +/// @param[in] mode One of the `glfw.Window.InputMode` enumerations. +/// @param[in] value The new value of the specified input mode. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: glfw.Window.getInputMode +pub inline fn setInputMode(self: Window, mode: InputMode, value: anytype) void { + internal_debug.assertInitialized(); + const T = @TypeOf(value); + std.debug.assert(switch (mode) { + .cursor => switch (@import("shims.zig").typeInfo(T)) { + .@"enum" => T == InputModeCursor, + .enum_literal => @hasField(InputModeCursor, @tagName(value)), + else => false, + }, + .sticky_keys => T == bool, + .sticky_mouse_buttons => T == bool, + .lock_key_mods => T == bool, + .raw_mouse_motion => T == bool, + }); + const int_value: c_int = switch (@import("shims.zig").typeInfo(T)) { + .@"enum", + .enum_literal, + => @intFromEnum(@as(InputModeCursor, value)), + else => @intFromBool(value), + }; + c.glfwSetInputMode(self.handle, @intFromEnum(mode), int_value); +} + +/// Returns the last reported press state of a keyboard key for the specified window. +/// +/// This function returns the last press state reported for the specified key to the specified +/// window. The returned state is one of `true` (pressed) or `false` (released). +/// +/// * `glfw.Action.repeat` is only reported to the key callback. +/// +/// If the `glfw.sticky_keys` input mode is enabled, this function returns `glfw.Action.press` the +/// first time you call it for a key that was pressed, even if that key has already been released. +/// +/// The key functions deal with physical keys, with key tokens (see keys) named after their use on +/// the standard US keyboard layout. If you want to input text, use the Unicode character callback +/// instead. +/// +/// The modifier key bit masks (see mods) are not key tokens and cannot be used with this function. +/// +/// __Do not use this function__ to implement text input, use glfw.Window.setCharCallback instead. +/// +/// @param[in] window The desired window. +/// @param[in] key The desired keyboard key (see keys). `glfw.key.unknown` is not a valid key for +/// this function. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: input_key +pub inline fn getKey(self: Window, key: Key) Action { + internal_debug.assertInitialized(); + const state = c.glfwGetKey(self.handle, @intFromEnum(key)); + return @as(Action, @enumFromInt(state)); +} + +/// Returns the last reported state of a mouse button for the specified window. +/// +/// This function returns whether the specified mouse button is pressed or not. +/// +/// If the glfw.sticky_mouse_buttons input mode is enabled, this function returns `true` the first +/// time you call it for a mouse button that was pressed, even if that mouse button has already been +/// released. +/// +/// @param[in] button The desired mouse button. +/// @return One of `true` (if pressed) or `false` (if released) +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: input_mouse_button +pub inline fn getMouseButton(self: Window, button: MouseButton) Action { + internal_debug.assertInitialized(); + const state = c.glfwGetMouseButton(self.handle, @intFromEnum(button)); + return @as(Action, @enumFromInt(state)); +} + +pub const CursorPos = struct { + xpos: f64, + ypos: f64, +}; + +/// Retrieves the position of the cursor relative to the content area of the window. +/// +/// This function returns the position of the cursor, in screen coordinates, relative to the +/// upper-left corner of the content area of the specified window. +/// +/// If the cursor is disabled (with `glfw.cursor_disabled`) then the cursor position is unbounded +/// and limited only by the minimum and maximum values of a `f64`. +/// +/// The coordinate can be converted to their integer equivalents with the `floor` function. Casting +/// directly to an integer type works for positive coordinates, but fails for negative ones. +/// +/// Any or all of the position arguments may be null. If an error occurs, all non-null position +/// arguments will be set to zero. +/// +/// @param[in] window The desired window. +/// @param[out] xpos Where to store the cursor x-coordinate, relative to the left edge of the +/// content area, or null. +/// @param[out] ypos Where to store the cursor y-coordinate, relative to the to top edge of the +/// content area, or null. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// Additionally returns a zero value in the event of an error. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_pos, glfw.Window.setCursorPos +pub inline fn getCursorPos(self: Window) CursorPos { + internal_debug.assertInitialized(); + var pos: CursorPos = undefined; + c.glfwGetCursorPos(self.handle, &pos.xpos, &pos.ypos); + return pos; +} + +/// Sets the position of the cursor, relative to the content area of the window. +/// +/// This function sets the position, in screen coordinates, of the cursor relative to the upper-left +/// corner of the content area of the specified window. The window must have input focus. If the +/// window does not have input focus when this function is called, it fails silently. +/// +/// __Do not use this function__ to implement things like camera controls. GLFW already provides the +/// `glfw.cursor_disabled` cursor mode that hides the cursor, transparently re-centers it and +/// provides unconstrained cursor motion. See glfw.Window.setInputMode for more information. +/// +/// If the cursor mode is `glfw.cursor_disabled` then the cursor position is unconstrained and +/// limited only by the minimum and maximum values of a `double`. +/// +/// @param[in] window The desired window. +/// @param[in] xpos The desired x-coordinate, relative to the left edge of the content area. +/// @param[in] ypos The desired y-coordinate, relative to the top edge of the content area. +/// +/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable. +/// +/// wayland: This function will only work when the cursor mode is `glfw.cursor_disabled`, otherwise +/// it will do nothing. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_pos, glfw.Window.getCursorPos +pub inline fn setCursorPos(self: Window, xpos: f64, ypos: f64) void { + internal_debug.assertInitialized(); + c.glfwSetCursorPos(self.handle, xpos, ypos); +} + +/// Sets the cursor for the window. +/// +/// This function sets the cursor image to be used when the cursor is over the content area of the +/// specified window. The set cursor will only be visible when the cursor mode (see cursor_mode) of +/// the window is `glfw.Cursor.normal`. +/// +/// On some platforms, the set cursor may not be visible unless the window also has input focus. +/// +/// @param[in] cursor The cursor to set, or null to switch back to the default arrow cursor. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_object +pub inline fn setCursor(self: Window, cursor: ?Cursor) void { + internal_debug.assertInitialized(); + c.glfwSetCursor(self.handle, if (cursor) |cs| cs.ptr else null); +} + +/// Sets the key callback. +/// +/// This function sets the key callback of the specified window, which is called when a key is +/// pressed, repeated or released. +/// +/// The key functions deal with physical keys, with layout independent key tokens (see keys) named +/// after their values in the standard US keyboard layout. If you want to input text, use the +/// character callback (see glfw.Window.setCharCallback) instead. +/// +/// When a window loses input focus, it will generate synthetic key release events for all pressed +/// keys. You can tell these events from user-generated events by the fact that the synthetic ones +/// are generated after the focus loss event has been processed, i.e. after the window focus +/// callback (see glfw.Window.setFocusCallback) has been called. +/// +/// The scancode of a key is specific to that platform or sometimes even to that machine. Scancodes +/// are intended to allow users to bind keys that don't have a GLFW key token. Such keys have `key` +/// set to `glfw.key.unknown`, their state is not saved and so it cannot be queried with +/// glfw.Window.getKey. +/// +/// Sometimes GLFW needs to generate synthetic key events, in which case the scancode may be zero. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new key callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] key The keyboard key (see keys) that was pressed or released. +/// @callback_param[in] scancode The platform-specific scancode of the key. +/// @callback_param[in] action `glfw.Action.press`, `glfw.Action.release` or `glfw.Action.repeat`. +/// Future releases may add more actions. +/// @callback_param[in] mods Bit field describing which modifier keys (see mods) were held down. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: input_key +pub inline fn setKeyCallback(self: Window, comptime callback: ?fn (window: Window, key: Key, scancode: i32, action: Action, mods: Mods) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn keyCallbackWrapper(handle: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(Key, @enumFromInt(key)), + @as(i32, @intCast(scancode)), + @as(Action, @enumFromInt(action)), + Mods.fromInt(mods), + }); + } + }; + + if (c.glfwSetKeyCallback(self.handle, CWrapper.keyCallbackWrapper) != null) return; + } else { + if (c.glfwSetKeyCallback(self.handle, null) != null) return; + } +} + +/// Sets the Unicode character callback. +/// +/// This function sets the character callback of the specified window, which is called when a +/// Unicode character is input. +/// +/// The character callback is intended for Unicode text input. As it deals with characters, it is +/// keyboard layout dependent, whereas the key callback (see glfw.Window.setKeyCallback) is not. +/// Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters. +/// If you want to know whether a specific physical key was pressed or released, see the key +/// callback instead. +/// +/// The character callback behaves as system text input normally does and will not be called if +/// modifier keys are held down that would prevent normal text input on that platform, for example a +/// Super (Command) key on macOS or Alt key on Windows. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] codepoint The Unicode code point of the character. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: input_char +pub inline fn setCharCallback(self: Window, comptime callback: ?fn (window: Window, codepoint: u21) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn charCallbackWrapper(handle: ?*c.GLFWwindow, codepoint: c_uint) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(u21, @intCast(codepoint)), + }); + } + }; + + if (c.glfwSetCharCallback(self.handle, CWrapper.charCallbackWrapper) != null) return; + } else { + if (c.glfwSetCharCallback(self.handle, null) != null) return; + } +} + +/// Sets the mouse button callback. +/// +/// This function sets the mouse button callback of the specified window, which is called when a +/// mouse button is pressed or released. +/// +/// When a window loses input focus, it will generate synthetic mouse button release events for all +/// pressed mouse buttons. You can tell these events from user-generated events by the fact that the +/// synthetic ones are generated after the focus loss event has been processed, i.e. after the +/// window focus callback (see glfw.Window.setFocusCallback) has been called. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] button The mouse button that was pressed or released. +/// @callback_param[in] action One of `glfw.Action.press` or `glfw.Action.release`. Future releases +/// may add more actions. +/// @callback_param[in] mods Bit field describing which modifier keys (see mods) were held down. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: input_mouse_button +pub inline fn setMouseButtonCallback(self: Window, comptime callback: ?fn (window: Window, button: MouseButton, action: Action, mods: Mods) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn mouseButtonCallbackWrapper(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as(MouseButton, @enumFromInt(button)), + @as(Action, @enumFromInt(action)), + Mods.fromInt(mods), + }); + } + }; + + if (c.glfwSetMouseButtonCallback(self.handle, CWrapper.mouseButtonCallbackWrapper) != null) return; + } else { + if (c.glfwSetMouseButtonCallback(self.handle, null) != null) return; + } +} + +/// Sets the cursor position callback. +/// +/// This function sets the cursor position callback of the specified window, which is called when +/// the cursor is moved. The callback is provided with the position, in screen coordinates, relative +/// to the upper-left corner of the content area of the window. +/// +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] xpos The new cursor x-coordinate, relative to the left edge of the content +/// area. +/// callback_@param[in] ypos The new cursor y-coordinate, relative to the top edge of the content +/// area. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_pos +pub inline fn setCursorPosCallback(self: Window, comptime callback: ?fn (window: Window, xpos: f64, ypos: f64) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn cursorPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: f64, ypos: f64) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + xpos, + ypos, + }); + } + }; + + if (c.glfwSetCursorPosCallback(self.handle, CWrapper.cursorPosCallbackWrapper) != null) return; + } else { + if (c.glfwSetCursorPosCallback(self.handle, null) != null) return; + } +} + +/// Sets the cursor enter/leave callback. +/// +/// This function sets the cursor boundary crossing callback of the specified window, which is +/// called when the cursor enters or leaves the content area of the window. +/// +/// @param[in] callback The new callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] entered `true` if the cursor entered the window's content area, or `false` +/// if it left it. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: cursor_enter +pub inline fn setCursorEnterCallback(self: Window, comptime callback: ?fn (window: Window, entered: bool) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn cursorEnterCallbackWrapper(handle: ?*c.GLFWwindow, entered: c_int) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + entered == c.GLFW_TRUE, + }); + } + }; + + if (c.glfwSetCursorEnterCallback(self.handle, CWrapper.cursorEnterCallbackWrapper) != null) return; + } else { + if (c.glfwSetCursorEnterCallback(self.handle, null) != null) return; + } +} + +/// Sets the scroll callback. +/// +/// This function sets the scroll callback of the specified window, which is called when a scrolling +/// device is used, such as a mouse wheel or scrolling area of a touchpad. +/// +/// The scroll callback receives all scrolling input, like that from a mouse wheel or a touchpad +/// scrolling area. +/// +/// @param[in] window The window whose callback to set. +/// @param[in] callback The new scroll callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] xoffset The scroll offset along the x-axis. +/// @callback_param[in] yoffset The scroll offset along the y-axis. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: scrolling +pub inline fn setScrollCallback(self: Window, comptime callback: ?fn (window: Window, xoffset: f64, yoffset: f64) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn scrollCallbackWrapper(handle: ?*c.GLFWwindow, xoffset: f64, yoffset: f64) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + xoffset, + yoffset, + }); + } + }; + + if (c.glfwSetScrollCallback(self.handle, CWrapper.scrollCallbackWrapper) != null) return; + } else { + if (c.glfwSetScrollCallback(self.handle, null) != null) return; + } +} + +/// Sets the path drop callback. +/// +/// This function sets the path drop callback of the specified window, which is called when one or +/// more dragged paths are dropped on the window. +/// +/// Because the path array and its strings may have been generated specifically for that event, they +/// are not guaranteed to be valid after the callback has returned. If you wish to use them after +/// the callback returns, you need to make a deep copy. +/// +/// @param[in] callback The new file drop callback, or null to remove the currently set callback. +/// +/// @callback_param[in] window The window that received the event. +/// @callback_param[in] path_count The number of dropped paths. +/// @callback_param[in] paths The UTF-8 encoded file and/or directory path names. +/// +/// @callback_pointer_lifetime The path array and its strings are valid until the callback function +/// returns. +/// +/// wayland: File drop is currently unimplemented. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: path_drop +pub inline fn setDropCallback(self: Window, comptime callback: ?fn (window: Window, paths: [][*:0]const u8) void) void { + internal_debug.assertInitialized(); + + if (callback) |user_callback| { + const CWrapper = struct { + pub fn dropCallbackWrapper(handle: ?*c.GLFWwindow, path_count: c_int, paths: [*c][*c]const u8) callconv(.C) void { + @call(.always_inline, user_callback, .{ + from(handle.?), + @as([*][*:0]const u8, @ptrCast(paths))[0..@as(u32, @intCast(path_count))], + }); + } + }; + + if (c.glfwSetDropCallback(self.handle, CWrapper.dropCallbackWrapper) != null) return; + } else { + if (c.glfwSetDropCallback(self.handle, null) != null) return; + } +} + +/// For testing purposes only; see glfw.Window.Hints and glfw.Window.create for the public API. +/// Sets the specified window hint to the desired value. +/// +/// This function sets hints for the next call to glfw.Window.create. The hints, once set, retain +/// their values until changed by a call to this function or glfw.window.defaultHints, or until the +/// library is terminated. +/// +/// This function does not check whether the specified hint values are valid. If you set hints to +/// invalid values this will instead be reported by the next call to glfw.createWindow. +/// +/// Some hints are platform specific. These may be set on any platform but they will only affect +/// their specific platform. Other platforms will ignore them. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum. +/// +/// @pointer_lifetime in the event that value is of a str type, the specified string is copied before this function returns. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: window_hints, glfw.Window.defaultHints +inline fn hint(h: Hint, value: anytype) void { + internal_debug.assertInitialized(); + const value_type = @TypeOf(value); + const value_type_info: @import("shims.zig").std.builtin.Type = @import("shims.zig").typeInfo(value_type); + + switch (value_type_info) { + .int, .comptime_int => { + c.glfwWindowHint(@intFromEnum(h), @as(c_int, @intCast(value))); + }, + .bool => { + const int_value = @intFromBool(value); + c.glfwWindowHint(@intFromEnum(h), @as(c_int, @intCast(int_value))); + }, + .@"enum" => { + const int_value = @intFromEnum(value); + c.glfwWindowHint(@intFromEnum(h), @as(c_int, @intCast(int_value))); + }, + .array => |arr_type| { + if (arr_type.child != u8) { + @compileError("expected array of u8, got " ++ @typeName(arr_type)); + } + c.glfwWindowHintString(@intFromEnum(h), &value[0]); + }, + .pointer => |pointer_info| { + const pointed_type = @import("shims.zig").typeInfo(pointer_info.child); + switch (pointed_type) { + .array => |arr_type| { + if (arr_type.child != u8) { + @compileError("expected pointer to array of u8, got " ++ @typeName(arr_type)); + } + }, + else => @compileError("expected pointer to array, got " ++ @typeName(pointed_type)), + } + + c.glfwWindowHintString(@intFromEnum(h), &value[0]); + }, + else => { + @compileError("expected a int, bool, enum, array, or pointer, got " ++ @typeName(value_type)); + }, + } +} + +test "defaultHints" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + defaultHints(); +} + +test "hint comptime int" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + hint(.focused, 1); + defaultHints(); +} + +test "hint int" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const focused: i32 = 1; + + hint(.focused, focused); + defaultHints(); +} + +test "hint bool" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + hint(.focused, true); + defaultHints(); +} + +test "hint enum(u1)" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const MyEnum = enum(u1) { + true = 1, + false = 0, + }; + + hint(.focused, MyEnum.true); + defaultHints(); +} + +test "hint enum(i32)" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const MyEnum = enum(i32) { + true = 1, + false = 0, + }; + + hint(.focused, MyEnum.true); + defaultHints(); +} + +test "hint array str" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const str_arr = [_]u8{ 'm', 'y', 'c', 'l', 'a', 's', 's' }; + + hint(.x11_class_name, str_arr); + defaultHints(); +} + +test "hint pointer str" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + hint(.x11_class_name, "myclass"); +} + +test "createWindow" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); +} + +test "setShouldClose" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + window.setShouldClose(true); + defer window.destroy(); +} + +test "setTitle" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setTitle("Updated title!"); +} + +test "setIcon" { + const allocator = testing.allocator; + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + // Create an all-red icon image. + const width: u32 = 48; + const height: u32 = 48; + const icon = try Image.init(allocator, width, height, width * height * 4); + var x: u32 = 0; + var y: u32 = 0; + while (y <= height) : (y += 1) { + while (x <= width) : (x += 1) { + icon.pixels[(x * y * 4) + 0] = 255; // red + icon.pixels[(x * y * 4) + 1] = 0; // green + icon.pixels[(x * y * 4) + 2] = 0; // blue + icon.pixels[(x * y * 4) + 3] = 255; // alpha + } + } + try window.setIcon(allocator, &[_]Image{icon}); + + icon.deinit(allocator); // glfw copies it. +} + +test "getPos" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getPos(); +} + +test "setPos" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.setPos(.{ .x = 0, .y = 0 }); +} + +test "getSize" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getSize(); +} + +test "setSize" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.setSize(.{ .width = 640, .height = 480 }); +} + +test "setSizeLimits" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setSizeLimits( + .{ .width = 720, .height = 480 }, + .{ .width = 1080, .height = 1920 }, + ); +} + +test "setAspectRatio" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setAspectRatio(4, 3); +} + +test "getFramebufferSize" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getFramebufferSize(); +} + +test "getFrameSize" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getFrameSize(); +} + +test "getContentScale" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getContentScale(); +} + +test "getOpacity" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getOpacity(); +} + +test "iconify" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.iconify(); +} + +test "restore" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.restore(); +} + +test "maximize" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.maximize(); +} + +test "show" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.show(); +} + +test "hide" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.hide(); +} + +test "focus" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.focus(); +} + +test "requestAttention" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.requestAttention(); +} + +test "swapBuffers" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.swapBuffers(); +} + +test "getMonitor" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getMonitor(); +} + +test "setMonitor" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setMonitor(null, 10, 10, 640, 480, 60); +} + +test "getAttrib" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getAttrib(.focused); +} + +test "setAttrib" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setAttrib(.decorated, false); +} + +test "setUserPointer" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + const T = struct { name: []const u8 }; + var my_value = T{ .name = "my window!" }; + + window.setUserPointer(&my_value); +} + +test "getUserPointer" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + const T = struct { name: []const u8 }; + var my_value = T{ .name = "my window!" }; + + window.setUserPointer(&my_value); + const got = window.getUserPointer(T); + std.debug.assert(&my_value == got); +} + +test "setPosCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setPosCallback((struct { + fn callback(_window: Window, xpos: i32, ypos: i32) void { + _ = _window; + _ = xpos; + _ = ypos; + } + }).callback); +} + +test "setSizeCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setSizeCallback((struct { + fn callback(_window: Window, width: i32, height: i32) void { + _ = _window; + _ = width; + _ = height; + } + }).callback); +} + +test "setCloseCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setCloseCallback((struct { + fn callback(_window: Window) void { + _ = _window; + } + }).callback); +} + +test "setRefreshCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setRefreshCallback((struct { + fn callback(_window: Window) void { + _ = _window; + } + }).callback); +} + +test "setFocusCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setFocusCallback((struct { + fn callback(_window: Window, focused: bool) void { + _ = _window; + _ = focused; + } + }).callback); +} + +test "setIconifyCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setIconifyCallback((struct { + fn callback(_window: Window, iconified: bool) void { + _ = _window; + _ = iconified; + } + }).callback); +} + +test "setMaximizeCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setMaximizeCallback((struct { + fn callback(_window: Window, maximized: bool) void { + _ = _window; + _ = maximized; + } + }).callback); +} + +test "setFramebufferSizeCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setFramebufferSizeCallback((struct { + fn callback(_window: Window, width: u32, height: u32) void { + _ = _window; + _ = width; + _ = height; + } + }).callback); +} + +test "setContentScaleCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setContentScaleCallback((struct { + fn callback(_window: Window, xscale: f32, yscale: f32) void { + _ = _window; + _ = xscale; + _ = yscale; + } + }).callback); +} + +test "setDropCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setDropCallback((struct { + fn callback(_window: Window, paths: [][*:0]const u8) void { + _ = _window; + _ = paths; + } + }).callback); +} + +test "getInputModeCursor" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputModeCursor(); +} + +test "setInputModeCursor" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setInputModeCursor(.hidden); +} + +test "getInputModeStickyKeys" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputModeStickyKeys(); +} + +test "setInputModeStickyKeys" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setInputModeStickyKeys(false); +} + +test "getInputModeStickyMouseButtons" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputModeStickyMouseButtons(); +} + +test "setInputModeStickyMouseButtons" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setInputModeStickyMouseButtons(false); +} + +test "getInputModeLockKeyMods" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputModeLockKeyMods(); +} + +test "setInputModeLockKeyMods" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setInputModeLockKeyMods(false); +} + +test "getInputModeRawMouseMotion" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputModeRawMouseMotion(); +} + +test "setInputModeRawMouseMotion" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setInputModeRawMouseMotion(false); +} + +test "getInputMode" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getInputMode(glfw.Window.InputMode.raw_mouse_motion) == 1; +} + +test "setInputMode" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + // Boolean values. + window.setInputMode(glfw.Window.InputMode.sticky_mouse_buttons, true); + + // Integer values. + window.setInputMode(glfw.Window.InputMode.cursor, glfw.Window.InputModeCursor.hidden); +} + +test "getKey" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getKey(glfw.Key.escape); +} + +test "getMouseButton" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getMouseButton(.left); +} + +test "getCursorPos" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + _ = window.getCursorPos(); +} + +test "setCursorPos" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setCursorPos(0, 0); +} + +test "setCursor" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + const cursor = glfw.Cursor.createStandard(.ibeam); + if (cursor) |cur| { + window.setCursor(cur); + defer cur.destroy(); + } +} + +test "setKeyCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setKeyCallback((struct { + fn callback(_window: Window, key: Key, scancode: i32, action: Action, mods: Mods) void { + _ = _window; + _ = key; + _ = scancode; + _ = action; + _ = mods; + } + }).callback); +} + +test "setCharCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setCharCallback((struct { + fn callback(_window: Window, codepoint: u21) void { + _ = _window; + _ = codepoint; + } + }).callback); +} + +test "setMouseButtonCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setMouseButtonCallback((struct { + fn callback(_window: Window, button: MouseButton, action: Action, mods: Mods) void { + _ = _window; + _ = button; + _ = action; + _ = mods; + } + }).callback); +} + +test "setCursorPosCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setCursorPosCallback((struct { + fn callback(_window: Window, xpos: f64, ypos: f64) void { + _ = _window; + _ = xpos; + _ = ypos; + } + }).callback); +} + +test "setCursorEnterCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setCursorEnterCallback((struct { + fn callback(_window: Window, entered: bool) void { + _ = _window; + _ = entered; + } + }).callback); +} + +test "setScrollCallback" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + window.setScrollCallback((struct { + fn callback(_window: Window, xoffset: f64, yoffset: f64) void { + _ = _window; + _ = xoffset; + _ = yoffset; + } + }).callback); +} + +test "hint-attribute default value parity" { + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + testing_ignore_window_hints_struct = true; + const window_a = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window_a: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window_a.destroy(); + + testing_ignore_window_hints_struct = false; + const window_b = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window_b: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window_b.destroy(); + + inline for (comptime std.enums.values(Window.Hint)) |hint_tag| { + if (@hasField(Window.Attrib, @tagName(hint_tag))) { + const attrib_tag = @field(Window.Attrib, @tagName(hint_tag)); + switch (attrib_tag) { + .resizable, + .visible, + .decorated, + .auto_iconify, + .floating, + .maximized, + .transparent_framebuffer, + .focus_on_show, + .mouse_passthrough, + .doublebuffer, + .client_api, + .context_creation_api, + .context_version_major, + .context_version_minor, + .context_robustness, + .context_release_behavior, + .context_no_error, // Note: at the time of writing this, GLFW does not list the default value for this hint in the documentation + .context_debug, + .opengl_forward_compat, + .opengl_profile, + => { + const expected = window_a.getAttrib(attrib_tag); + const actual = window_b.getAttrib(attrib_tag); + + testing.expectEqual(expected, actual) catch |err| { + std.debug.print("On attribute '{}'.\n", .{hint_tag}); + return err; + }; + }, + + // This attribute is based on a check for which window is currently in focus, + // and the default value, as of writing this comment, is 'true', which means + // that first window_a takes focus, and then window_b takes focus, meaning + // that we can't actually test for the default value. + .focused => continue, + + .iconified, + .hovered, + .context_revision, + => unreachable, + } + } + // Future: we could consider hint values that can't be retrieved via attributes: + // center_cursor + // mouse_passthrough + // scale_to_monitor + // red_bits + // green_bits + // blue_bits + // alpha_bits + // depth_bits + // stencil_bits + // accum_red_bits + // accum_green_bits + // accum_blue_bits + // accum_alpha_bits + // aux_buffers + // samples + + // refresh_rate + // stereo + // srgb_capable + // doublebuffer + + // platform specific, and thus not considered: + // cocoa_retina_framebuffer + // cocoa_frame_name + // cocoa_graphics_switching + } +} diff --git a/pkg/glfw/action.zig b/pkg/glfw/action.zig new file mode 100644 index 000000000..59314709f --- /dev/null +++ b/pkg/glfw/action.zig @@ -0,0 +1,13 @@ +const c = @import("c.zig").c; + +/// Key and button actions +pub const Action = enum(c_int) { + /// The key or mouse button was released. + release = c.GLFW_RELEASE, + + /// The key or mouse button was pressed. + press = c.GLFW_PRESS, + + /// The key was held down until it repeated. + repeat = c.GLFW_REPEAT, +}; diff --git a/pkg/glfw/allocator.zig b/pkg/glfw/allocator.zig new file mode 100644 index 000000000..d6517cf3e --- /dev/null +++ b/pkg/glfw/allocator.zig @@ -0,0 +1,143 @@ +// TODO: implement custom allocator support + +// /*! @brief +// * +// * @sa @ref init_allocator +// * @sa @ref glfwInitAllocator +// * +// * @since Added in version 3.4. +// * +// * @ingroup init +// */ +// typedef struct GLFWallocator +// { +// GLFWallocatefun allocate; +// GLFWreallocatefun reallocate; +// GLFWdeallocatefun deallocate; +// void* user; +// } GLFWallocator; + +// /*! @brief The function pointer type for memory allocation callbacks. +// * +// * This is the function pointer type for memory allocation callbacks. A memory +// * allocation callback function has the following signature: +// * @code +// * void* function_name(size_t size, void* user) +// * @endcode +// * +// * This function must return either a memory block at least `size` bytes long, +// * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation +// * failures gracefully yet. +// * +// * This function may be called during @ref glfwInit but before the library is +// * flagged as initialized, as well as during @ref glfwTerminate after the +// * library is no longer flagged as initialized. +// * +// * Any memory allocated by this function will be deallocated during library +// * termination or earlier. +// * +// * The size will always be greater than zero. Allocations of size zero are filtered out +// * before reaching the custom allocator. +// * +// * @param[in] size The minimum size, in bytes, of the memory block. +// * @param[in] user The user-defined pointer from the allocator. +// * @return The address of the newly allocated memory block, or `NULL` if an +// * error occurred. +// * +// * @pointer_lifetime The returned memory block must be valid at least until it +// * is deallocated. +// * +// * @reentrancy This function should not call any GLFW function. +// * +// * @thread_safety This function may be called from any thread that calls GLFW functions. +// * +// * @sa @ref init_allocator +// * @sa @ref GLFWallocator +// * +// * @since Added in version 3.4. +// * +// * @ingroup init +// */ +// typedef void* (* GLFWallocatefun)(size_t size, void* user); + +// /*! @brief The function pointer type for memory reallocation callbacks. +// * +// * This is the function pointer type for memory reallocation callbacks. +// * A memory reallocation callback function has the following signature: +// * @code +// * void* function_name(void* block, size_t size, void* user) +// * @endcode +// * +// * This function must return a memory block at least `size` bytes long, or +// * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation +// * failures gracefully yet. +// * +// * This function may be called during @ref glfwInit but before the library is +// * flagged as initialized, as well as during @ref glfwTerminate after the +// * library is no longer flagged as initialized. +// * +// * Any memory allocated by this function will be deallocated during library +// * termination or earlier. +// * +// * The block address will never be `NULL` and the size will always be greater than zero. +// * Reallocations of a block to size zero are converted into deallocations. Reallocations +// * of `NULL` to a non-zero size are converted into regular allocations. +// * +// * @param[in] block The address of the memory block to reallocate. +// * @param[in] size The new minimum size, in bytes, of the memory block. +// * @param[in] user The user-defined pointer from the allocator. +// * @return The address of the newly allocated or resized memory block, or +// * `NULL` if an error occurred. +// * +// * @pointer_lifetime The returned memory block must be valid at least until it +// * is deallocated. +// * +// * @reentrancy This function should not call any GLFW function. +// * +// * @thread_safety This function may be called from any thread that calls GLFW functions. +// * +// * @sa @ref init_allocator +// * @sa @ref GLFWallocator +// * +// * @since Added in version 3.4. +// * +// * @ingroup init +// */ +// typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user); + +// /*! @brief The function pointer type for memory deallocation callbacks. +// * +// * This is the function pointer type for memory deallocation callbacks. +// * A memory deallocation callback function has the following signature: +// * @code +// * void function_name(void* block, void* user) +// * @endcode +// * +// * This function may deallocate the specified memory block. This memory block +// * will have been allocated with the same allocator. +// * +// * This function may be called during @ref glfwInit but before the library is +// * flagged as initialized, as well as during @ref glfwTerminate after the +// * library is no longer flagged as initialized. +// * +// * The block address will never be `NULL`. Deallocations of `NULL` are filtered out +// * before reaching the custom allocator. +// * +// * @param[in] block The address of the memory block to deallocate. +// * @param[in] user The user-defined pointer from the allocator. +// * +// * @pointer_lifetime The specified memory block will not be accessed by GLFW +// * after this function is called. +// * +// * @reentrancy This function should not call any GLFW function. +// * +// * @thread_safety This function may be called from any thread that calls GLFW functions. +// * +// * @sa @ref init_allocator +// * @sa @ref GLFWallocator +// * +// * @since Added in version 3.4. +// * +// * @ingroup init +// */ +// typedef void (* GLFWdeallocatefun)(void* block, void* user); diff --git a/pkg/glfw/build.zig b/pkg/glfw/build.zig new file mode 100644 index 000000000..cc61f18b2 --- /dev/null +++ b/pkg/glfw/build.zig @@ -0,0 +1,272 @@ +const std = @import("std"); +const apple_sdk = @import("apple_sdk"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const module = b.addModule("glfw", .{ + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + }); + + const lib = try buildLib(b, module, .{ + .target = target, + .optimize = optimize, + }); + + const test_exe: ?*std.Build.Step.Compile = if (target.query.isNative()) exe: { + const exe = b.addTest(.{ + .name = "test", + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + }); + if (target.result.os.tag.isDarwin()) { + try apple_sdk.addPaths(b, exe.root_module); + } + + const tests_run = b.addRunArtifact(exe); + const test_step = b.step("test", "Run tests"); + test_step.dependOn(&tests_run.step); + + // Uncomment this if we're debugging tests + b.installArtifact(exe); + + break :exe exe; + } else null; + + if (b.systemIntegrationOption("glfw3", .{})) { + module.linkSystemLibrary("glfw3", dynamic_link_opts); + if (test_exe) |exe| exe.linkSystemLibrary2("glfw3", dynamic_link_opts); + } else { + module.linkLibrary(lib); + b.installArtifact(lib); + if (test_exe) |exe| exe.linkLibrary(lib); + } +} + +fn buildLib( + b: *std.Build, + module: *std.Build.Module, + options: anytype, +) !*std.Build.Step.Compile { + const target = options.target; + const optimize = options.optimize; + + const use_x11 = b.option( + bool, + "x11", + "Build with X11. Only useful on Linux", + ) orelse true; + const use_wl = b.option( + bool, + "wayland", + "Build with Wayland. Only useful on Linux", + ) orelse true; + + const use_opengl = b.option( + bool, + "opengl", + "Build with OpenGL; deprecated on MacOS", + ) orelse false; + const use_gles = b.option( + bool, + "gles", + "Build with GLES; not supported on MacOS", + ) orelse false; + const use_metal = b.option( + bool, + "metal", + "Build with Metal; only supported on MacOS", + ) orelse true; + + const lib = b.addStaticLibrary(.{ + .name = "glfw", + .target = target, + .optimize = optimize, + }); + lib.linkLibC(); + + const upstream = b.lazyDependency("glfw", .{}) orelse return lib; + lib.addIncludePath(upstream.path("include")); + module.addIncludePath(upstream.path("include")); + lib.installHeadersDirectory(upstream.path("include/GLFW"), "GLFW", .{}); + + switch (target.result.os.tag) { + .windows => { + lib.linkSystemLibrary("gdi32"); + lib.linkSystemLibrary("user32"); + lib.linkSystemLibrary("shell32"); + + if (use_opengl) { + lib.linkSystemLibrary("opengl32"); + } + + if (use_gles) { + lib.linkSystemLibrary("GLESv3"); + } + + const flags = [_][]const u8{"-D_GLFW_WIN32"}; + lib.addCSourceFiles(.{ + .root = upstream.path(""), + .files = &base_sources, + .flags = &flags, + }); + lib.addCSourceFiles(.{ + .root = upstream.path(""), + .files = &windows_sources, + .flags = &flags, + }); + }, + + .macos => { + try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, module); + + // Transitive dependencies, explicit linkage of these works around + // ziglang/zig#17130 + lib.linkFramework("CFNetwork"); + lib.linkFramework("ApplicationServices"); + lib.linkFramework("ColorSync"); + lib.linkFramework("CoreText"); + lib.linkFramework("ImageIO"); + + // Direct dependencies + lib.linkSystemLibrary("objc"); + lib.linkFramework("IOKit"); + lib.linkFramework("CoreFoundation"); + lib.linkFramework("AppKit"); + lib.linkFramework("CoreServices"); + lib.linkFramework("CoreGraphics"); + lib.linkFramework("Foundation"); + + if (use_metal) { + lib.linkFramework("Metal"); + } + + if (use_opengl) { + lib.linkFramework("OpenGL"); + } + + const flags = [_][]const u8{"-D_GLFW_COCOA"}; + lib.addCSourceFiles(.{ + .root = upstream.path(""), + .files = &base_sources, + .flags = &flags, + }); + lib.addCSourceFiles(.{ + .root = upstream.path(""), + .files = &macos_sources, + .flags = &flags, + }); + }, + + // everything that isn't windows or mac is linux :P + else => { + var sources = std.BoundedArray([]const u8, 64).init(0) catch unreachable; + var flags = std.BoundedArray([]const u8, 16).init(0) catch unreachable; + + sources.appendSlice(&base_sources) catch unreachable; + sources.appendSlice(&linux_sources) catch unreachable; + + if (use_x11) { + lib.linkSystemLibrary2("X11", dynamic_link_opts); + lib.linkSystemLibrary2("xkbcommon", dynamic_link_opts); + sources.appendSlice(&linux_x11_sources) catch unreachable; + flags.append("-D_GLFW_X11") catch unreachable; + } + + if (use_wl) { + lib.linkSystemLibrary2("wayland-client", dynamic_link_opts); + + lib.root_module.addCMacro("WL_MARSHAL_FLAG_DESTROY", "1"); + lib.addIncludePath(b.path("wayland-headers")); + + sources.appendSlice(&linux_wl_sources) catch unreachable; + flags.append("-D_GLFW_WAYLAND") catch unreachable; + flags.append("-Wno-implicit-function-declaration") catch unreachable; + } + + lib.addCSourceFiles(.{ + .root = upstream.path(""), + .files = sources.slice(), + .flags = flags.slice(), + }); + }, + } + + return lib; +} + +// For dynamic linking, we prefer dynamic linking and to search by +// mode first. Mode first will search all paths for a dynamic library +// before falling back to static. +const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, +}; + +const base_sources = [_][]const u8{ + "src/context.c", + "src/egl_context.c", + "src/init.c", + "src/input.c", + "src/monitor.c", + "src/null_init.c", + "src/null_joystick.c", + "src/null_monitor.c", + "src/null_window.c", + "src/osmesa_context.c", + "src/platform.c", + "src/vulkan.c", + "src/window.c", +}; + +const linux_sources = [_][]const u8{ + "src/linux_joystick.c", + "src/posix_module.c", + "src/posix_poll.c", + "src/posix_thread.c", + "src/posix_time.c", + "src/xkb_unicode.c", +}; + +const linux_wl_sources = [_][]const u8{ + "src/wl_init.c", + "src/wl_monitor.c", + "src/wl_window.c", +}; + +const linux_x11_sources = [_][]const u8{ + "src/glx_context.c", + "src/x11_init.c", + "src/x11_monitor.c", + "src/x11_window.c", +}; + +const windows_sources = [_][]const u8{ + "src/wgl_context.c", + "src/win32_init.c", + "src/win32_joystick.c", + "src/win32_module.c", + "src/win32_monitor.c", + "src/win32_thread.c", + "src/win32_time.c", + "src/win32_window.c", +}; + +const macos_sources = [_][]const u8{ + // C sources + "src/cocoa_time.c", + "src/posix_module.c", + "src/posix_thread.c", + + // ObjC sources + "src/cocoa_init.m", + "src/cocoa_joystick.m", + "src/cocoa_monitor.m", + "src/cocoa_window.m", + "src/nsgl_context.m", +}; diff --git a/pkg/glfw/build.zig.zon b/pkg/glfw/build.zig.zon new file mode 100644 index 000000000..6fd6c81a8 --- /dev/null +++ b/pkg/glfw/build.zig.zon @@ -0,0 +1,15 @@ +.{ + .name = .glfw, + .version = "3.4.0", + .fingerprint = 0x3bbe0a5c667e2c62, + .paths = .{""}, + .dependencies = .{ + .glfw = .{ + .url = "https://github.com/glfw/glfw/archive/73948e6c0f15b1053cf74b7c4e6b04fd36e97e29.zip", + .hash = "N-V-__8AADTkRwBjUvVwTLOnV96QhN0J5Nyg7YzvnISe-Eax", + .lazy = true, + }, + + .apple_sdk = .{ .path = "../apple-sdk" }, + }, +} diff --git a/pkg/glfw/c.zig b/pkg/glfw/c.zig new file mode 100644 index 000000000..58599025b --- /dev/null +++ b/pkg/glfw/c.zig @@ -0,0 +1,6 @@ +pub const c = @cImport({ + // Must be uncommented for vulkan.zig to work + // @cDefine("GLFW_INCLUDE_VULKAN", "1"); + @cDefine("GLFW_INCLUDE_NONE", "1"); + @cInclude("GLFW/glfw3.h"); +}); diff --git a/pkg/glfw/clipboard.zig b/pkg/glfw/clipboard.zig new file mode 100644 index 000000000..a7e2d0e2f --- /dev/null +++ b/pkg/glfw/clipboard.zig @@ -0,0 +1,71 @@ +const std = @import("std"); + +const c = @import("c.zig").c; + +const internal_debug = @import("internal_debug.zig"); + +/// Sets the clipboard to the specified string. +/// +/// This function sets the system clipboard to the specified, UTF-8 encoded string. +/// +/// @param[in] string A UTF-8 encoded string. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @pointer_lifetime The specified string is copied before this function returns. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: clipboard, glfwGetClipboardString +pub inline fn setClipboardString(value: [*:0]const u8) void { + internal_debug.assertInitialized(); + c.glfwSetClipboardString(null, value); +} + +/// Returns the contents of the clipboard as a string. +/// +/// This function returns the contents of the system clipboard, if it contains or is convertible to +/// a UTF-8 encoded string. If the clipboard is empty or if its contents cannot be converted, +/// glfw.ErrorCode.FormatUnavailable is returned. +/// +/// @return The contents of the clipboard as a UTF-8 encoded string. +/// +/// Possible errors include glfw.ErrorCode.FormatUnavailable and glfw.ErrorCode.PlatformError. +/// null is returned in the event of an error. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is valid until the next call to glfw.getClipboardString or glfw.setClipboardString +/// or until the library is terminated. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: clipboard, glfwSetClipboardString +pub inline fn getClipboardString() ?[:0]const u8 { + internal_debug.assertInitialized(); + if (c.glfwGetClipboardString(null)) |c_str| return std.mem.span(@as([*:0]const u8, @ptrCast(c_str))); + return null; +} + +test "setClipboardString" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + glfw.setClipboardString("hello mach"); +} + +test "getClipboardString" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.getClipboardString(); +} diff --git a/pkg/glfw/errors.zig b/pkg/glfw/errors.zig new file mode 100644 index 000000000..ce98ec5cd --- /dev/null +++ b/pkg/glfw/errors.zig @@ -0,0 +1,338 @@ +//! Errors + +const testing = @import("std").testing; +const mem = @import("std").mem; +const c = @import("c.zig").c; + +/// Errors that GLFW can produce. +pub const ErrorCode = error{ + /// GLFW has not been initialized. + /// + /// This occurs if a GLFW function was called that must not be called unless the library is + /// initialized. + NotInitialized, + + /// No context is current for this thread. + /// + /// This occurs if a GLFW function was called that needs and operates on the current OpenGL or + /// OpenGL ES context but no context is current on the calling thread. One such function is + /// glfw.SwapInterval. + NoCurrentContext, + + /// One of the arguments to the function was an invalid enum value. + /// + /// One of the arguments to the function was an invalid enum value, for example requesting + /// glfw.red_bits with glfw.getWindowAttrib. + InvalidEnum, + + /// One of the arguments to the function was an invalid value. + /// + /// One of the arguments to the function was an invalid value, for example requesting a + /// non-existent OpenGL or OpenGL ES version like 2.7. + /// + /// Requesting a valid but unavailable OpenGL or OpenGL ES version will instead result in a + /// glfw.ErrorCode.VersionUnavailable error. + InvalidValue, + + /// A memory allocation failed. + OutOfMemory, + + /// GLFW could not find support for the requested API on the system. + /// + /// The installed graphics driver does not support the requested API, or does not support it + /// via the chosen context creation API. Below are a few examples. + /// + /// Some pre-installed Windows graphics drivers do not support OpenGL. AMD only supports + /// OpenGL ES via EGL, while Nvidia and Intel only support it via a WGL or GLX extension. macOS + /// does not provide OpenGL ES at all. The Mesa EGL, OpenGL and OpenGL ES libraries do not + /// interface with the Nvidia binary driver. Older graphics drivers do not support Vulkan. + APIUnavailable, + + /// The requested OpenGL or OpenGL ES version (including any requested context or framebuffer + /// hints) is not available on this machine. + /// + /// The machine does not support your requirements. If your application is sufficiently + /// flexible, downgrade your requirements and try again. Otherwise, inform the user that their + /// machine does not match your requirements. + /// + /// Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 comes out + /// before the 4.x series gets that far, also fail with this error and not glfw.ErrorCode.InvalidValue, + /// because GLFW cannot know what future versions will exist. + VersionUnavailable, + + /// A platform-specific error occurred that does not match any of the more specific categories. + /// + /// A bug or configuration error in GLFW, the underlying operating system or its drivers, or a + /// lack of required resources. Report the issue to our [issue tracker](https://github.com/glfw/glfw/issues). + PlatformError, + + /// The requested format is not supported or available. + /// + /// If emitted during window creation, the requested pixel format is not supported. + /// + /// If emitted when querying the clipboard, the contents of the clipboard could not be + /// converted to the requested format. + /// + /// If emitted during window creation, one or more hard constraints did not match any of the + /// available pixel formats. If your application is sufficiently flexible, downgrade your + /// requirements and try again. Otherwise, inform the user that their machine does not match + /// your requirements. + /// + /// If emitted when querying the clipboard, ignore the error or report it to the user, as + /// appropriate. + FormatUnavailable, + + /// The specified window does not have an OpenGL or OpenGL ES context. + /// + /// A window that does not have an OpenGL or OpenGL ES context was passed to a function that + /// requires it to have one. + NoWindowContext, + + /// The specified cursor shape is not available. + /// + /// The specified standard cursor shape is not available, either because the + /// current platform cursor theme does not provide it or because it is not + /// available on the platform. + /// + /// analysis: Platform or system settings limitation. Pick another standard cursor shape or + /// create a custom cursor. + CursorUnavailable, + + /// The requested feature is not provided by the platform. + /// + /// The requested feature is not provided by the platform, so GLFW is unable to + /// implement it. The documentation for each function notes if it could emit + /// this error. + /// + /// analysis: Platform or platform version limitation. The error can be ignored + /// unless the feature is critical to the application. + /// + /// A function call that emits this error has no effect other than the error and + /// updating any existing out parameters. + /// + FeatureUnavailable, + + /// The requested feature is not implemented for the platform. + /// + /// The requested feature has not yet been implemented in GLFW for this platform. + /// + /// analysis: An incomplete implementation of GLFW for this platform, hopefully + /// fixed in a future release. The error can be ignored unless the feature is + /// critical to the application. + /// + /// A function call that emits this error has no effect other than the error and + /// updating any existing out parameters. + /// + FeatureUnimplemented, + + /// Platform unavailable or no matching platform was found. + /// + /// If emitted during initialization, no matching platform was found. If glfw.InitHint.platform + /// is set to `.any_platform`, GLFW could not detect any of the platforms supported by this + /// library binary, except for the Null platform. If set to a specific platform, it is either + /// not supported by this library binary or GLFW was not able to detect it. + /// + /// If emitted by a native access function, GLFW was initialized for a different platform + /// than the function is for. + /// + /// analysis: Failure to detect any platform usually only happens on non-macOS Unix + /// systems, either when no window system is running or the program was run from + /// a terminal that does not have the necessary environment variables. Fall back to + /// a different platform if possible or notify the user that no usable platform was + /// detected. + /// + /// Failure to detect a specific platform may have the same cause as above or be because + /// support for that platform was not compiled in. Call glfw.platformSupported to + /// check whether a specific platform is supported by a library binary. + /// + PlatformUnavailable, +}; + +/// An error produced by GLFW and the description associated with it. +pub const Error = struct { + error_code: ErrorCode, + description: [:0]const u8, +}; + +fn convertError(e: c_int) ErrorCode!void { + return switch (e) { + c.GLFW_NO_ERROR => {}, + c.GLFW_NOT_INITIALIZED => ErrorCode.NotInitialized, + c.GLFW_NO_CURRENT_CONTEXT => ErrorCode.NoCurrentContext, + c.GLFW_INVALID_ENUM => ErrorCode.InvalidEnum, + c.GLFW_INVALID_VALUE => ErrorCode.InvalidValue, + c.GLFW_OUT_OF_MEMORY => ErrorCode.OutOfMemory, + c.GLFW_API_UNAVAILABLE => ErrorCode.APIUnavailable, + c.GLFW_VERSION_UNAVAILABLE => ErrorCode.VersionUnavailable, + c.GLFW_PLATFORM_ERROR => ErrorCode.PlatformError, + c.GLFW_FORMAT_UNAVAILABLE => ErrorCode.FormatUnavailable, + c.GLFW_NO_WINDOW_CONTEXT => ErrorCode.NoWindowContext, + c.GLFW_CURSOR_UNAVAILABLE => ErrorCode.CursorUnavailable, + c.GLFW_FEATURE_UNAVAILABLE => ErrorCode.FeatureUnavailable, + c.GLFW_FEATURE_UNIMPLEMENTED => ErrorCode.FeatureUnimplemented, + c.GLFW_PLATFORM_UNAVAILABLE => ErrorCode.PlatformUnavailable, + else => unreachable, + }; +} + +/// Clears the last error and the error description pointer for the calling thread. Does nothing if +/// no error has occurred since the last call. +/// +/// @remark This function may be called before @ref glfwInit. +/// +/// @thread_safety This function may be called from any thread. +pub inline fn clearError() void { + _ = c.glfwGetError(null); +} + +/// Returns and clears the last error for the calling thread. +/// +/// This function returns and clears the error code of the last error that occurred on the calling +/// thread, along with a UTF-8 encoded human-readable description of it. If no error has occurred +/// since the last call, it returns GLFW_NO_ERROR (zero) and the description pointer is set to +/// `NULL`. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is guaranteed to be valid only until the next error occurs or the library is +/// terminated. +/// +/// @remark This function may be called before @ref glfwInit. +/// +/// @thread_safety This function may be called from any thread. +pub inline fn getError() ?Error { + var desc: [*c]const u8 = null; + convertError(c.glfwGetError(&desc)) catch |error_code| { + return .{ + .error_code = error_code, + .description = mem.sliceTo(desc, 0), + }; + }; + return null; +} + +pub inline fn mustGetError() Error { + return getError() orelse { + @panic("glfw: mustGetError called but no error is present"); + }; +} + +/// Returns and clears the last error for the calling thread. +/// +/// This function returns and clears the error code of the last error that occurred on the calling +/// thread. If no error has occurred since the last call, it returns GLFW_NO_ERROR (zero). +/// +/// @return The last error code for the calling thread, or @ref GLFW_NO_ERROR (zero). +/// +/// @remark This function may be called before @ref glfwInit. +/// +/// @thread_safety This function may be called from any thread. +pub inline fn getErrorCode() ErrorCode!void { + return convertError(c.glfwGetError(null)); +} + +/// Returns and clears the last error code for the calling thread. If no error is present, this +/// function panics. +pub inline fn mustGetErrorCode() ErrorCode { + try getErrorCode(); + @panic("glfw: mustGetErrorCode called but no error is present"); +} + +/// Returns and clears the last error description for the calling thread. +/// +/// This function returns a UTF-8 encoded human-readable description of the last error that occured +/// on the calling thread. If no error has occurred since the last call, it returns null. +/// +/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it +/// yourself. It is guaranteed to be valid only until the next error occurs or the library is +/// terminated. +/// +/// @remark This function may be called before @ref glfwInit. +/// +/// @thread_safety This function may be called from any thread. +pub inline fn getErrorString() ?[:0]const u8 { + var desc: [*c]const u8 = null; + const error_code = c.glfwGetError(&desc); + if (error_code != c.GLFW_NO_ERROR) { + return mem.sliceTo(desc, 0); + } + return null; +} + +/// Returns and clears the last error description for the calling thread. If no error is present, +/// this function panics. +pub inline fn mustGetErrorString() [:0]const u8 { + return getErrorString() orelse { + @panic("glfw: mustGetErrorString called but no error is present"); + }; +} + +/// Sets the error callback. +/// +/// This function sets the error callback, which is called with an error code +/// and a human-readable description each time a GLFW error occurs. +/// +/// The error code is set before the callback is called. Calling @ref +/// glfwGetError from the error callback will return the same value as the error +/// code argument. +/// +/// The error callback is called on the thread where the error occurred. If you +/// are using GLFW from multiple threads, your error callback needs to be +/// written accordingly. +/// +/// Because the description string may have been generated specifically for that +/// error, it is not guaranteed to be valid after the callback has returned. If +/// you wish to use it after the callback returns, you need to make a copy. +/// +/// Once set, the error callback remains set even after the library has been +/// terminated. +/// +/// @param[in] callback The new callback, or `NULL` to remove the currently set +/// callback. +/// +/// @callback_param `error_code` An error code. Future releases may add more error codes. +/// @callback_param `description` A UTF-8 encoded string describing the error. +/// +/// @errors None. +/// +/// @remark This function may be called before @ref glfwInit. +/// +/// @thread_safety This function must only be called from the main thread. +pub fn setErrorCallback(comptime callback: ?fn (error_code: ErrorCode, description: [:0]const u8) void) void { + if (callback) |user_callback| { + const CWrapper = struct { + pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.C) void { + convertError(err_int) catch |error_code| { + user_callback(error_code, mem.sliceTo(c_description, 0)); + }; + } + }; + + _ = c.glfwSetErrorCallback(CWrapper.errorCallbackWrapper); + return; + } + + _ = c.glfwSetErrorCallback(null); +} + +test "set error callback" { + const TestStruct = struct { + pub fn callback(_: ErrorCode, _: [:0]const u8) void {} + }; + setErrorCallback(TestStruct.callback); +} + +test "error string" { + try testing.expect(getErrorString() == null); +} + +test "error code" { + try getErrorCode(); +} + +test "error code and string" { + try testing.expect(getError() == null); +} + +test "clear error" { + clearError(); +} diff --git a/pkg/glfw/gamepad_axis.zig b/pkg/glfw/gamepad_axis.zig new file mode 100644 index 000000000..97fdf3748 --- /dev/null +++ b/pkg/glfw/gamepad_axis.zig @@ -0,0 +1,16 @@ +const c = @import("c.zig").c; + +/// Gamepad axes. +/// +/// See glfw.getGamepadState for how these are used. +pub const GamepadAxis = enum(c_int) { + left_x = c.GLFW_GAMEPAD_AXIS_LEFT_X, + left_y = c.GLFW_GAMEPAD_AXIS_LEFT_Y, + right_x = c.GLFW_GAMEPAD_AXIS_RIGHT_X, + right_y = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, + left_trigger = c.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, + right_trigger = c.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, +}; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const last = GamepadAxis.right_trigger; diff --git a/pkg/glfw/gamepad_button.zig b/pkg/glfw/gamepad_button.zig new file mode 100644 index 000000000..ac47ebea0 --- /dev/null +++ b/pkg/glfw/gamepad_button.zig @@ -0,0 +1,37 @@ +const c = @import("c.zig").c; + +/// Gamepad buttons. +/// +/// See glfw.getGamepadState for how these are used. +pub const GamepadButton = enum(c_int) { + a = c.GLFW_GAMEPAD_BUTTON_A, + b = c.GLFW_GAMEPAD_BUTTON_B, + x = c.GLFW_GAMEPAD_BUTTON_X, + y = c.GLFW_GAMEPAD_BUTTON_Y, + left_bumper = c.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, + right_bumper = c.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, + back = c.GLFW_GAMEPAD_BUTTON_BACK, + start = c.GLFW_GAMEPAD_BUTTON_START, + guide = c.GLFW_GAMEPAD_BUTTON_GUIDE, + left_thumb = c.GLFW_GAMEPAD_BUTTON_LEFT_THUMB, + right_thumb = c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, + dpad_up = c.GLFW_GAMEPAD_BUTTON_DPAD_UP, + dpad_right = c.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, + dpad_down = c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN, + dpad_left = c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT, +}; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const last = GamepadButton.dpad_left; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const cross = GamepadButton.a; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const circle = GamepadButton.b; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const square = GamepadButton.x; + +/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden. +pub const triangle = GamepadButton.y; diff --git a/pkg/glfw/hat.zig b/pkg/glfw/hat.zig new file mode 100644 index 000000000..ffbb4a1c8 --- /dev/null +++ b/pkg/glfw/hat.zig @@ -0,0 +1,100 @@ +const c = @import("c.zig").c; + +// must be in sync with GLFW C constants in hat state group, search for "@defgroup hat_state Joystick hat states" +/// A bitmask of all Joystick hat states +/// +/// See glfw.Joystick.getHats for how these are used. +pub const Hat = packed struct(u8) { + up: bool = false, + right: bool = false, + down: bool = false, + left: bool = false, + _padding: u4 = 0, + + pub inline fn centered(self: Hat) bool { + return self.up == false and self.right == false and self.down == false and self.left == false; + } + + inline fn verifyIntType(comptime IntType: type) void { + comptime { + switch (@import("shims.zig").typeInfo(IntType)) { + .int => {}, + else => @compileError("Int was not of int type"), + } + } + } + + pub inline fn toInt(self: Hat, comptime IntType: type) IntType { + verifyIntType(IntType); + return @as(IntType, @intCast(@as(u8, @bitCast(self)))); + } + + pub inline fn fromInt(flags: anytype) Hat { + verifyIntType(@TypeOf(flags)); + return @as(Hat, @bitCast(@as(u8, @intCast(flags)))); + } +}; + +/// Holds all GLFW hat values in their raw form. +pub const RawHat = struct { + pub const centered = c.GLFW_HAT_CENTERED; + pub const up = c.GLFW_HAT_UP; + pub const right = c.GLFW_HAT_RIGHT; + pub const down = c.GLFW_HAT_DOWN; + pub const left = c.GLFW_HAT_LEFT; + + pub const right_up = right | up; + pub const right_down = right | down; + pub const left_up = left | up; + pub const left_down = left | down; +}; + +test "from int, single" { + const std = @import("std"); + + try std.testing.expectEqual(Hat{ + .up = true, + .right = false, + .down = false, + .left = false, + ._padding = 0, + }, Hat.fromInt(RawHat.up)); +} + +test "from int, multi" { + const std = @import("std"); + + try std.testing.expectEqual(Hat{ + .up = true, + .right = false, + .down = true, + .left = true, + ._padding = 0, + }, Hat.fromInt(RawHat.up | RawHat.down | RawHat.left)); +} + +test "to int, single" { + const std = @import("std"); + + var v = Hat{ + .up = true, + .right = false, + .down = false, + .left = false, + ._padding = 0, + }; + try std.testing.expectEqual(v.toInt(c_int), RawHat.up); +} + +test "to int, multi" { + const std = @import("std"); + + var v = Hat{ + .up = true, + .right = false, + .down = true, + .left = true, + ._padding = 0, + }; + try std.testing.expectEqual(v.toInt(c_int), RawHat.up | RawHat.down | RawHat.left); +} diff --git a/pkg/glfw/internal_debug.zig b/pkg/glfw/internal_debug.zig new file mode 100644 index 000000000..6e0ab4f1e --- /dev/null +++ b/pkg/glfw/internal_debug.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const is_debug = builtin.mode == .Debug; +var glfw_initialized = if (is_debug) false else @as(void, {}); +pub inline fn toggleInitialized() void { + if (is_debug) glfw_initialized = !glfw_initialized; +} +pub inline fn assertInitialized() void { + if (is_debug) std.debug.assert(glfw_initialized); +} +pub inline fn assumeInitialized() void { + if (is_debug) glfw_initialized = true; +} diff --git a/pkg/glfw/key.zig b/pkg/glfw/key.zig new file mode 100644 index 000000000..27b13119c --- /dev/null +++ b/pkg/glfw/key.zig @@ -0,0 +1,266 @@ +//! Keyboard key IDs. +//! +//! See glfw.setKeyCallback for how these are used. +//! +//! These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), but re-arranged to +//! map to 7-bit ASCII for printable keys (function keys are put in the 256+ range). +//! +//! The naming of the key codes follow these rules: +//! +//! - The US keyboard layout is used +//! - Names of printable alphanumeric characters are used (e.g. "a", "r", "three", etc.) +//! - For non-alphanumeric characters, Unicode:ish names are used (e.g. "comma", "left_bracket", +//! etc.). Note that some names do not correspond to the Unicode standard (usually for brevity) +//! - Keys that lack a clear US mapping are named "world_x" +//! - For non-printable keys, custom names are used (e.g. "F4", "backspace", etc.) + +const std = @import("std"); + +const cc = @import("c.zig").c; + +const internal_debug = @import("internal_debug.zig"); + +/// enum containing all glfw keys +pub const Key = enum(c_int) { + /// The unknown key + unknown = cc.GLFW_KEY_UNKNOWN, + + /// Printable keys + space = cc.GLFW_KEY_SPACE, + apostrophe = cc.GLFW_KEY_APOSTROPHE, + comma = cc.GLFW_KEY_COMMA, + minus = cc.GLFW_KEY_MINUS, + period = cc.GLFW_KEY_PERIOD, + slash = cc.GLFW_KEY_SLASH, + zero = cc.GLFW_KEY_0, + one = cc.GLFW_KEY_1, + two = cc.GLFW_KEY_2, + three = cc.GLFW_KEY_3, + four = cc.GLFW_KEY_4, + five = cc.GLFW_KEY_5, + six = cc.GLFW_KEY_6, + seven = cc.GLFW_KEY_7, + eight = cc.GLFW_KEY_8, + nine = cc.GLFW_KEY_9, + semicolon = cc.GLFW_KEY_SEMICOLON, + equal = cc.GLFW_KEY_EQUAL, + a = cc.GLFW_KEY_A, + b = cc.GLFW_KEY_B, + c = cc.GLFW_KEY_C, + d = cc.GLFW_KEY_D, + e = cc.GLFW_KEY_E, + f = cc.GLFW_KEY_F, + g = cc.GLFW_KEY_G, + h = cc.GLFW_KEY_H, + i = cc.GLFW_KEY_I, + j = cc.GLFW_KEY_J, + k = cc.GLFW_KEY_K, + l = cc.GLFW_KEY_L, + m = cc.GLFW_KEY_M, + n = cc.GLFW_KEY_N, + o = cc.GLFW_KEY_O, + p = cc.GLFW_KEY_P, + q = cc.GLFW_KEY_Q, + r = cc.GLFW_KEY_R, + s = cc.GLFW_KEY_S, + t = cc.GLFW_KEY_T, + u = cc.GLFW_KEY_U, + v = cc.GLFW_KEY_V, + w = cc.GLFW_KEY_W, + x = cc.GLFW_KEY_X, + y = cc.GLFW_KEY_Y, + z = cc.GLFW_KEY_Z, + left_bracket = cc.GLFW_KEY_LEFT_BRACKET, + backslash = cc.GLFW_KEY_BACKSLASH, + right_bracket = cc.GLFW_KEY_RIGHT_BRACKET, + grave_accent = cc.GLFW_KEY_GRAVE_ACCENT, + world_1 = cc.GLFW_KEY_WORLD_1, // non-US #1 + world_2 = cc.GLFW_KEY_WORLD_2, // non-US #2 + + // Function keys + escape = cc.GLFW_KEY_ESCAPE, + enter = cc.GLFW_KEY_ENTER, + tab = cc.GLFW_KEY_TAB, + backspace = cc.GLFW_KEY_BACKSPACE, + insert = cc.GLFW_KEY_INSERT, + delete = cc.GLFW_KEY_DELETE, + right = cc.GLFW_KEY_RIGHT, + left = cc.GLFW_KEY_LEFT, + down = cc.GLFW_KEY_DOWN, + up = cc.GLFW_KEY_UP, + page_up = cc.GLFW_KEY_PAGE_UP, + page_down = cc.GLFW_KEY_PAGE_DOWN, + home = cc.GLFW_KEY_HOME, + end = cc.GLFW_KEY_END, + caps_lock = cc.GLFW_KEY_CAPS_LOCK, + scroll_lock = cc.GLFW_KEY_SCROLL_LOCK, + num_lock = cc.GLFW_KEY_NUM_LOCK, + print_screen = cc.GLFW_KEY_PRINT_SCREEN, + pause = cc.GLFW_KEY_PAUSE, + F1 = cc.GLFW_KEY_F1, + F2 = cc.GLFW_KEY_F2, + F3 = cc.GLFW_KEY_F3, + F4 = cc.GLFW_KEY_F4, + F5 = cc.GLFW_KEY_F5, + F6 = cc.GLFW_KEY_F6, + F7 = cc.GLFW_KEY_F7, + F8 = cc.GLFW_KEY_F8, + F9 = cc.GLFW_KEY_F9, + F10 = cc.GLFW_KEY_F10, + F11 = cc.GLFW_KEY_F11, + F12 = cc.GLFW_KEY_F12, + F13 = cc.GLFW_KEY_F13, + F14 = cc.GLFW_KEY_F14, + F15 = cc.GLFW_KEY_F15, + F16 = cc.GLFW_KEY_F16, + F17 = cc.GLFW_KEY_F17, + F18 = cc.GLFW_KEY_F18, + F19 = cc.GLFW_KEY_F19, + F20 = cc.GLFW_KEY_F20, + F21 = cc.GLFW_KEY_F21, + F22 = cc.GLFW_KEY_F22, + F23 = cc.GLFW_KEY_F23, + F24 = cc.GLFW_KEY_F24, + F25 = cc.GLFW_KEY_F25, + kp_0 = cc.GLFW_KEY_KP_0, + kp_1 = cc.GLFW_KEY_KP_1, + kp_2 = cc.GLFW_KEY_KP_2, + kp_3 = cc.GLFW_KEY_KP_3, + kp_4 = cc.GLFW_KEY_KP_4, + kp_5 = cc.GLFW_KEY_KP_5, + kp_6 = cc.GLFW_KEY_KP_6, + kp_7 = cc.GLFW_KEY_KP_7, + kp_8 = cc.GLFW_KEY_KP_8, + kp_9 = cc.GLFW_KEY_KP_9, + kp_decimal = cc.GLFW_KEY_KP_DECIMAL, + kp_divide = cc.GLFW_KEY_KP_DIVIDE, + kp_multiply = cc.GLFW_KEY_KP_MULTIPLY, + kp_subtract = cc.GLFW_KEY_KP_SUBTRACT, + kp_add = cc.GLFW_KEY_KP_ADD, + kp_enter = cc.GLFW_KEY_KP_ENTER, + kp_equal = cc.GLFW_KEY_KP_EQUAL, + left_shift = cc.GLFW_KEY_LEFT_SHIFT, + left_control = cc.GLFW_KEY_LEFT_CONTROL, + left_alt = cc.GLFW_KEY_LEFT_ALT, + left_super = cc.GLFW_KEY_LEFT_SUPER, + right_shift = cc.GLFW_KEY_RIGHT_SHIFT, + right_control = cc.GLFW_KEY_RIGHT_CONTROL, + right_alt = cc.GLFW_KEY_RIGHT_ALT, + right_super = cc.GLFW_KEY_RIGHT_SUPER, + menu = cc.GLFW_KEY_MENU, + + pub inline fn last() Key { + return @as(Key, @enumFromInt(cc.GLFW_KEY_LAST)); + } + + /// Returns the layout-specific name of the specified printable key. + /// + /// This function returns the name of the specified printable key, encoded as UTF-8. This is + /// typically the character that key would produce without any modifier keys, intended for + /// displaying key bindings to the user. For dead keys, it is typically the diacritic it would add + /// to a character. + /// + /// __Do not use this function__ for text input (see input_char). You will break text input for many + /// languages even if it happens to work for yours. + /// + /// If the key is `glfw.key.unknown`, the scancode is used to identify the key, otherwise the + /// scancode is ignored. If you specify a non-printable key, or `glfw.key.unknown` and a scancode + /// that maps to a non-printable key, this function returns null but does not emit an error. + /// + /// This behavior allows you to always pass in the arguments in the key callback (see input_key) + /// without modification. + /// + /// The printable keys are: + /// + /// - `glfw.Key.apostrophe` + /// - `glfw.Key.comma` + /// - `glfw.Key.minus` + /// - `glfw.Key.period` + /// - `glfw.Key.slash` + /// - `glfw.Key.semicolon` + /// - `glfw.Key.equal` + /// - `glfw.Key.left_bracket` + /// - `glfw.Key.right_bracket` + /// - `glfw.Key.backslash` + /// - `glfw.Key.world_1` + /// - `glfw.Key.world_2` + /// - `glfw.Key.0` to `glfw.key.9` + /// - `glfw.Key.a` to `glfw.key.z` + /// - `glfw.Key.kp_0` to `glfw.key.kp_9` + /// - `glfw.Key.kp_decimal` + /// - `glfw.Key.kp_divide` + /// - `glfw.Key.kp_multiply` + /// - `glfw.Key.kp_subtract` + /// - `glfw.Key.kp_add` + /// - `glfw.Key.kp_equal` + /// + /// Names for printable keys depend on keyboard layout, while names for non-printable keys are the + /// same across layouts but depend on the application language and should be localized along with + /// other user interface text. + /// + /// @param[in] key The key to query, or `glfw.key.unknown`. + /// @param[in] scancode The scancode of the key to query. + /// @return The UTF-8 encoded, layout-specific name of the key, or null. + /// + /// Possible errors include glfw.ErrorCode.PlatformError. + /// Also returns null in the event of an error. + /// + /// The contents of the returned string may change when a keyboard layout change event is received. + /// + /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it + /// yourself. It is valid until the library is terminated. + /// + /// @thread_safety This function must only be called from the main thread. + /// + /// see also: input_key_name + pub inline fn getName(self: Key, scancode: i32) ?[:0]const u8 { + internal_debug.assertInitialized(); + const name_opt = cc.glfwGetKeyName(@intFromEnum(self), @as(c_int, @intCast(scancode))); + return if (name_opt) |name| + std.mem.span(@as([*:0]const u8, @ptrCast(name))) + else + null; + } + + /// Returns the platform-specific scancode of the specified key. + /// + /// This function returns the platform-specific scancode of the specified key. + /// + /// If the key is `glfw.key.UNKNOWN` or does not exist on the keyboard this method will return `-1`. + /// + /// @param[in] key Any named key (see keys). + /// @return The platform-specific scancode for the key. + /// + /// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError. + /// Additionally returns -1 in the event of an error. + /// + /// @thread_safety This function may be called from any thread. + pub inline fn getScancode(self: Key) i32 { + internal_debug.assertInitialized(); + return cc.glfwGetKeyScancode(@intFromEnum(self)); + } +}; + +test "getName" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.Key.a.getName(0); +} + +test "getScancode" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.Key.a.getScancode(); +} diff --git a/pkg/glfw/main.zig b/pkg/glfw/main.zig new file mode 100644 index 000000000..329f17ed2 --- /dev/null +++ b/pkg/glfw/main.zig @@ -0,0 +1,586 @@ +const std = @import("std"); +const testing = std.testing; + +const c = @import("c.zig").c; + +const key = @import("key.zig"); +const errors = @import("errors.zig"); + +/// Possible value for various window hints, etc. +pub const dont_care = c.GLFW_DONT_CARE; + +pub const getError = errors.getError; +pub const mustGetError = errors.mustGetError; +pub const getErrorCode = errors.getErrorCode; +pub const mustGetErrorCode = errors.mustGetErrorCode; +pub const getErrorString = errors.getErrorString; +pub const mustGetErrorString = errors.mustGetErrorString; +pub const setErrorCallback = errors.setErrorCallback; +pub const clearError = errors.clearError; +pub const ErrorCode = errors.ErrorCode; +pub const Error = errors.Error; + +pub const Action = @import("action.zig").Action; +pub const GamepadAxis = @import("gamepad_axis.zig").GamepadAxis; +pub const GamepadButton = @import("gamepad_button.zig").GamepadButton; +pub const gamepad_axis = @import("gamepad_axis.zig"); +pub const gamepad_button = @import("gamepad_button.zig"); +pub const GammaRamp = @import("GammaRamp.zig"); +pub const Image = @import("Image.zig"); +pub const Joystick = @import("Joystick.zig"); +pub const Monitor = @import("Monitor.zig"); +pub const mouse_button = @import("mouse_button.zig"); +pub const MouseButton = mouse_button.MouseButton; +pub const version = @import("version.zig"); +pub const VideoMode = @import("VideoMode.zig"); +pub const Window = @import("Window.zig"); +pub const Cursor = @import("Cursor.zig"); +pub const Native = @import("native.zig").Native; +pub const BackendOptions = @import("native.zig").BackendOptions; +pub const Key = key.Key; +pub const setClipboardString = @import("clipboard.zig").setClipboardString; +pub const getClipboardString = @import("clipboard.zig").getClipboardString; +pub const makeContextCurrent = @import("opengl.zig").makeContextCurrent; +pub const getCurrentContext = @import("opengl.zig").getCurrentContext; +pub const swapInterval = @import("opengl.zig").swapInterval; +pub const extensionSupported = @import("opengl.zig").extensionSupported; +pub const GLProc = @import("opengl.zig").GLProc; +pub const getProcAddress = @import("opengl.zig").getProcAddress; +pub const getTime = @import("time.zig").getTime; +pub const setTime = @import("time.zig").setTime; +pub const getTimerValue = @import("time.zig").getTimerValue; +pub const getTimerFrequency = @import("time.zig").getTimerFrequency; +pub const Hat = @import("hat.zig").Hat; +pub const RawHat = @import("hat.zig").RawHat; +pub const Mods = @import("mod.zig").Mods; +pub const RawMods = @import("mod.zig").RawMods; + +const internal_debug = @import("internal_debug.zig"); + +/// If GLFW was already initialized in your program, e.g. you are embedding Zig code into an existing +/// program that has already called glfwInit via the C API for you - then you need to tell mach/glfw +/// that it has in fact been initialized already, otherwise when you call other methods mach/glfw +/// would panic thinking glfw.init has not been called yet. +pub fn assumeInitialized() void { + internal_debug.assumeInitialized(); +} + +/// Initializes the GLFW library. +/// +/// This function initializes the GLFW library. Before most GLFW functions can be used, GLFW must +/// be initialized, and before an application terminates GLFW should be terminated in order to free +/// any resources allocated during or after initialization. +/// +/// If this function fails, it calls glfw.Terminate before returning. If it succeeds, you should +/// call glfw.Terminate before the application exits. +/// +/// Additional calls to this function after successful initialization but before termination will +/// return immediately with no error. +/// +/// The glfw.InitHint.platform init hint controls which platforms are considered during +/// initialization. This also depends on which platforms the library was compiled to support. +/// +/// macos: This function will change the current directory of the application to the +/// `Contents/Resources` subdirectory of the application's bundle, if present. This can be disabled +/// with `glfw.InitHint.cocoa_chdir_resources`. +/// +/// macos: This function will create the main menu and dock icon for the application. If GLFW finds +/// a `MainMenu.nib` it is loaded and assumed to contain a menu bar. Otherwise a minimal menu bar is +/// created manually with common commands like Hide, Quit and About. The About entry opens a minimal +/// about dialog with information from the application's bundle. The menu bar and dock icon can be +/// disabled entirely with `glfw.InitHint.cocoa_menubar`. +/// +/// x11: This function will set the `LC_CTYPE` category of the application locale according to the +/// current environment if that category is still "C". This is because the "C" locale breaks +/// Unicode text input. +/// +/// Possible errors include glfw.ErrorCode.PlatformUnavailable, glfw.ErrorCode.PlatformError. +/// Returns a bool indicating success. +/// +/// @thread_safety This function must only be called from the main thread. +pub inline fn init(hints: InitHints) bool { + internal_debug.toggleInitialized(); + internal_debug.assertInitialized(); + errdefer { + internal_debug.assertInitialized(); + internal_debug.toggleInitialized(); + } + + inline for (comptime std.meta.fieldNames(InitHints)) |field_name| { + const init_hint = @field(InitHint, field_name); + const init_value = @field(hints, field_name); + if (@TypeOf(init_value) == PlatformType) { + initHint(init_hint, @intFromEnum(init_value)); + } else { + initHint(init_hint, init_value); + } + } + + return c.glfwInit() == c.GLFW_TRUE; +} + +// TODO: implement custom allocator support +// +// /*! @brief Sets the init allocator to the desired value. +// * +// * To use the default allocator, call this function with a `NULL` argument. +// * +// * If you specify an allocator struct, every member must be a valid function +// * pointer. If any member is `NULL`, this function emits @ref +// * GLFW_INVALID_VALUE and the init allocator is unchanged. +// * +// * @param[in] allocator The allocator to use at the next initialization, or +// * `NULL` to use the default one. +// * +// * @errors Possible errors include @ref GLFW_INVALID_VALUE. +// * +// * @pointer_lifetime The specified allocator is copied before this function +// * returns. +// * +// * @thread_safety This function must only be called from the main thread. +// * +// * @sa @ref init_allocator +// * @sa @ref glfwInit +// * +// * @since Added in version 3.4. +// * +// * @ingroup init +// */ +// GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator); + +/// Terminates the GLFW library. +/// +/// This function destroys all remaining windows and cursors, restores any modified gamma ramps +/// and frees any other allocated resources. Once this function is called, you must again call +/// glfw.init successfully before you will be able to use most GLFW functions. +/// +/// If GLFW has been successfully initialized, this function should be called before the +/// application exits. If initialization fails, there is no need to call this function, as it is +/// called by glfw.init before it returns failure. +/// +/// This function has no effect if GLFW is not initialized. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// warning: The contexts of any remaining windows must not be current on any other thread when +/// this function is called. +/// +/// reentrancy: This function must not be called from a callback. +/// +/// thread_safety: This function must only be called from the main thread. +pub inline fn terminate() void { + internal_debug.assertInitialized(); + internal_debug.toggleInitialized(); + c.glfwTerminate(); +} + +/// Initialization hints for passing into glfw.init +pub const InitHints = struct { + /// Specifies whether to also expose joystick hats as buttons, for compatibility with earlier + /// versions of GLFW that did not have glfwGetJoystickHats. + joystick_hat_buttons: bool = true, + + /// macOS specific init hint. Ignored on other platforms. + /// + /// Specifies whether to set the current directory to the application to the Contents/Resources + /// subdirectory of the application's bundle, if present. + cocoa_chdir_resources: bool = true, + + /// macOS specific init hint. Ignored on other platforms. + /// + /// specifies whether to create a basic menu bar, either from a nib or manually, when the first + /// window is created, which is when AppKit is initialized. + cocoa_menubar: bool = true, + + /// Platform selection init hint. + /// + /// Possible values are `PlatformType` enums. + platform: PlatformType = .any, +}; + +/// Initialization hints for passing into glfw.initHint +const InitHint = enum(c_int) { + /// Specifies whether to also expose joystick hats as buttons, for compatibility with earlier + /// versions of GLFW that did not have glfwGetJoystickHats. + /// + /// Possible values are `true` and `false`. + joystick_hat_buttons = c.GLFW_JOYSTICK_HAT_BUTTONS, + + /// ANGLE rendering backend init hint. + /// + /// Possible values are `AnglePlatformType` enums. + angle_platform_type = c.GLFW_ANGLE_PLATFORM_TYPE, + + /// Platform selection init hint. + /// + /// Possible values are `PlatformType` enums. + platform = c.GLFW_PLATFORM, + + /// macOS specific init hint. Ignored on other platforms. + /// + /// Specifies whether to set the current directory to the application to the Contents/Resources + /// subdirectory of the application's bundle, if present. + /// + /// Possible values are `true` and `false`. + cocoa_chdir_resources = c.GLFW_COCOA_CHDIR_RESOURCES, + + /// macOS specific init hint. Ignored on other platforms. + /// + /// specifies whether to create a basic menu bar, either from a nib or manually, when the first + /// window is created, which is when AppKit is initialized. + /// + /// Possible values are `true` and `false`. + cocoa_menubar = c.GLFW_COCOA_MENUBAR, + + /// X11 specific init hint. + x11_xcb_vulkan_surface = c.GLFW_X11_XCB_VULKAN_SURFACE, + + /// Wayland specific init hint. + /// + /// Possible values are `WaylandLibdecorInitHint` enums. + wayland_libdecor = c.GLFW_WAYLAND_LIBDECOR, +}; + +/// Angle platform type hints for glfw.InitHint.angle_platform_type +pub const AnglePlatformType = enum(c_int) { + none = c.GLFW_ANGLE_PLATFORM_TYPE_NONE, + opengl = c.GLFW_ANGLE_PLATFORM_TYPE_OPENGL, + opengles = c.GLFW_ANGLE_PLATFORM_TYPE_OPENGLES, + d3d9 = c.GLFW_ANGLE_PLATFORM_TYPE_D3D9, + d3d11 = c.GLFW_ANGLE_PLATFORM_TYPE_D3D11, + vulkan = c.GLFW_ANGLE_PLATFORM_TYPE_VULKAN, + metal = c.GLFW_ANGLE_PLATFORM_TYPE_METAL, +}; + +/// Wayland libdecor hints for glfw.InitHint.wayland_libdecor +/// +/// libdecor is important for GNOME, since GNOME does not implement server side decorations on +/// wayland. libdecor is loaded dynamically at runtime, so in general enabling it is always +/// safe to do. It is enabled by default. +pub const WaylandLibdecorInitHint = enum(c_int) { + wayland_prefer_libdecor = c.GLFW_WAYLAND_PREFER_LIBDECOR, + wayland_disable_libdecor = c.GLFW_WAYLAND_DISABLE_LIBDECOR, +}; + +/// Platform type hints for glfw.InitHint.platform +pub const PlatformType = enum(c_int) { + /// Enables automatic platform detection. + /// Will default to X11 on wayland. + any = c.GLFW_ANY_PLATFORM, + win32 = c.GLFW_PLATFORM_WIN32, + cocoa = c.GLFW_PLATFORM_COCOA, + wayland = c.GLFW_PLATFORM_WAYLAND, + x11 = c.GLFW_PLATFORM_X11, + null = c.GLFW_PLATFORM_NULL, +}; + +/// Sets the specified init hint to the desired value. +/// +/// This function sets hints for the next initialization of GLFW. +/// +/// The values you set hints to are never reset by GLFW, but they only take effect during +/// initialization. Once GLFW has been initialized, any values you set will be ignored until the +/// library is terminated and initialized again. +/// +/// Some hints are platform specific. These may be set on any platform but they will only affect +/// their specific platform. Other platforms will ignore them. Setting these hints requires no +/// platform specific headers or functions. +/// +/// @param hint: The init hint to set. +/// @param value: The new value of the init hint. +/// +/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.InvalidValue. +/// +/// @remarks This function may be called before glfw.init. +/// +/// @thread_safety This function must only be called from the main thread. +fn initHint(hint: InitHint, value: anytype) void { + switch (@import("shims.zig").typeInfo(@TypeOf(value))) { + .int, .comptime_int => { + c.glfwInitHint(@intFromEnum(hint), @as(c_int, @intCast(value))); + }, + .bool => c.glfwInitHint(@intFromEnum(hint), @as(c_int, @intCast(@intFromBool(value)))), + else => @compileError("expected a int or bool, got " ++ @typeName(@TypeOf(value))), + } +} + +/// Returns a string describing the compile-time configuration. +/// +/// This function returns the compile-time generated version string of the GLFW library binary. It +/// describes the version, platform, compiler and any platform or operating system specific +/// compile-time options. It should not be confused with the OpenGL or OpenGL ES version string, +/// queried with `glGetString`. +/// +/// __Do not use the version string__ to parse the GLFW library version. Use the glfw.version +/// constants instead. +/// +/// __Do not use the version string__ to parse what platforms are supported. The +/// `glfw.platformSupported` function lets you query platform support. +/// +/// returns: The ASCII encoded GLFW version string. +/// +/// remark: This function may be called before @ref glfw.Init. +/// +/// pointer_lifetime: The returned string is static and compile-time generated. +/// +/// thread_safety: This function may be called from any thread. +pub inline fn getVersionString() [:0]const u8 { + return std.mem.span(@as([*:0]const u8, @ptrCast(c.glfwGetVersionString()))); +} + +/// Returns the currently selected platform. +/// +/// This function returns the platform that was selected during initialization. The returned value +/// will be one of `glfw.PlatformType.win32`, `glfw.PlatformType.cocoa`, +/// `glfw.PlatformType.wayland`, `glfw.PlatformType.x11` or `glfw.PlatformType.null`. +/// +/// thread_safety: This function may be called from any thread. +pub fn getPlatform() PlatformType { + internal_debug.assertInitialized(); + return @as(PlatformType, @enumFromInt(c.glfwGetPlatform())); +} + +/// Returns whether the library includes support for the specified platform. +/// +/// This function returns whether the library was compiled with support for the specified platform. +/// The platform must be one of `glfw.PlatformType.win32`, `glfw.PlatformType.cocoa`, +/// `glfw.PlatformType.wayland`, `glfw.PlatformType.x11` or `glfw.PlatformType.null`. +/// +/// remark: This function may be called before glfw.Init. +/// +/// thread_safety: This function may be called from any thread. +pub fn platformSupported(platform: PlatformType) bool { + internal_debug.assertInitialized(); + return c.glfwPlatformSupported(@intFromEnum(platform)) == c.GLFW_TRUE; +} + +/// Processes all pending events. +/// +/// This function processes only those events that are already in the event queue and then returns +/// immediately. Processing events will cause the window and input callbacks associated with those +/// events to be called. +/// +/// On some platforms, a window move, resize or menu operation will cause event processing to +/// block. This is due to how event processing is designed on those platforms. You can use the +/// window refresh callback (see window_refresh) to redraw the contents of your window when +/// necessary during such operations. +/// +/// Do not assume that callbacks you set will _only_ be called in response to event processing +/// functions like this one. While it is necessary to poll for events, window systems that require +/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system +/// function calls. GLFW will pass those events on to the application callbacks before returning. +/// +/// Event processing is not required for joystick input to work. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @reentrancy This function must not be called from a callback. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout +pub inline fn pollEvents() void { + internal_debug.assertInitialized(); + c.glfwPollEvents(); +} + +/// Waits until events are queued and processes them. +/// +/// This function puts the calling thread to sleep until at least one event is available in the +/// event queue. Once one or more events are available, it behaves exactly like glfw.pollEvents, +/// i.e. the events in the queue are processed and the function then returns immediately. +/// Processing events will cause the window and input callbacks associated with those events to be +/// called. +/// +/// Since not all events are associated with callbacks, this function may return without a callback +/// having been called even if you are monitoring all callbacks. +/// +/// On some platforms, a window move, resize or menu operation will cause event processing to +/// block. This is due to how event processing is designed on those platforms. You can use the +/// window refresh callback (see window_refresh) to redraw the contents of your window when +/// necessary during such operations. +/// +/// Do not assume that callbacks you set will _only_ be called in response to event processing +/// functions like this one. While it is necessary to poll for events, window systems that require +/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system +/// function calls. GLFW will pass those events on to the application callbacks before returning. +/// +/// Event processing is not required for joystick input to work. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @reentrancy This function must not be called from a callback. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: events, glfw.pollEvents, glfw.waitEventsTimeout +pub inline fn waitEvents() void { + internal_debug.assertInitialized(); + c.glfwWaitEvents(); +} + +/// Waits with timeout until events are queued and processes them. +/// +/// This function puts the calling thread to sleep until at least one event is available in the +/// event queue, or until the specified timeout is reached. If one or more events are available, it +/// behaves exactly like glfw.pollEvents, i.e. the events in the queue are processed and the +/// function then returns immediately. Processing events will cause the window and input callbacks +/// associated with those events to be called. +/// +/// The timeout value must be a positive finite number. +/// +/// Since not all events are associated with callbacks, this function may return without a callback +/// having been called even if you are monitoring all callbacks. +/// +/// On some platforms, a window move, resize or menu operation will cause event processing to +/// block. This is due to how event processing is designed on those platforms. You can use the +/// window refresh callback (see window_refresh) to redraw the contents of your window when +/// necessary during such operations. +/// +/// Do not assume that callbacks you set will _only_ be called in response to event processing +/// functions like this one. While it is necessary to poll for events, window systems that require +/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system +/// function calls. GLFW will pass those events on to the application callbacks before returning. +/// +/// Event processing is not required for joystick input to work. +/// +/// @param[in] timeout The maximum amount of time, in seconds, to wait. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue and glfw.ErrorCode.PlatformError. +/// +/// @reentrancy This function must not be called from a callback. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: events, glfw.pollEvents, glfw.waitEvents +pub inline fn waitEventsTimeout(timeout: f64) void { + internal_debug.assertInitialized(); + std.debug.assert(!std.math.isNan(timeout)); + std.debug.assert(timeout >= 0); + std.debug.assert(timeout <= std.math.floatMax(f64)); + c.glfwWaitEventsTimeout(timeout); +} + +/// Posts an empty event to the event queue. +/// +/// This function posts an empty event from the current thread to the event queue, causing +/// glfw.waitEvents or glfw.waitEventsTimeout to return. +/// +/// Possible errors include glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout +pub inline fn postEmptyEvent() void { + internal_debug.assertInitialized(); + c.glfwPostEmptyEvent(); +} + +/// Returns whether raw mouse motion is supported. +/// +/// This function returns whether raw mouse motion is supported on the current system. This status +/// does not change after GLFW has been initialized so you only need to check this once. If you +/// attempt to enable raw motion on a system that does not support it, glfw.ErrorCode.PlatformError +/// will be emitted. +/// +/// Raw mouse motion is closer to the actual motion of the mouse across a surface. It is not +/// affected by the scaling and acceleration applied to the motion of the desktop cursor. That +/// processing is suitable for a cursor while raw motion is better for controlling for example a 3D +/// camera. Because of this, raw mouse motion is only provided when the cursor is disabled. +/// +/// @return `true` if raw mouse motion is supported on the current machine, or `false` otherwise. +/// +/// @thread_safety This function must only be called from the main thread. +/// +/// see also: raw_mouse_motion, glfw.setInputMode +pub inline fn rawMouseMotionSupported() bool { + internal_debug.assertInitialized(); + return c.glfwRawMouseMotionSupported() == c.GLFW_TRUE; +} + +pub fn basicTest() !void { + defer clearError(); // clear any error we generate + if (!init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()}); + std.process.exit(1); + } + defer terminate(); + + const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + const start = std.time.milliTimestamp(); + while (std.time.milliTimestamp() < start + 1000 and !window.shouldClose()) { + c.glfwPollEvents(); + } +} + +test { + std.testing.refAllDeclsRecursive(@This()); +} + +test "getVersionString" { + std.debug.print("\nGLFW version v{}.{}.{}\n", .{ version.major, version.minor, version.revision }); + std.debug.print("\nstring: {s}\n", .{getVersionString()}); +} + +test "init" { + _ = init(.{ .cocoa_chdir_resources = true }); + if (getErrorString()) |err| { + std.log.err("failed to initialize GLFW: {?s}", .{err}); + std.process.exit(1); + } + defer terminate(); +} + +test "pollEvents" { + defer clearError(); // clear any error we generate + if (!init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()}); + std.process.exit(1); + } + defer terminate(); + + pollEvents(); +} + +test "waitEventsTimeout" { + defer clearError(); // clear any error we generate + if (!init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()}); + std.process.exit(1); + } + defer terminate(); + + waitEventsTimeout(0.25); +} + +test "postEmptyEvent_and_waitEvents" { + defer clearError(); // clear any error we generate + if (!init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()}); + std.process.exit(1); + } + defer terminate(); + + postEmptyEvent(); + waitEvents(); +} + +test "rawMouseMotionSupported" { + defer clearError(); // clear any error we generate + if (!init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()}); + std.process.exit(1); + } + defer terminate(); + + _ = rawMouseMotionSupported(); +} + +test "basic" { + try basicTest(); +} diff --git a/pkg/glfw/mod.zig b/pkg/glfw/mod.zig new file mode 100644 index 000000000..aa9c9371f --- /dev/null +++ b/pkg/glfw/mod.zig @@ -0,0 +1,167 @@ +//! Modifier key flags +//! +//! See glfw.setKeyCallback for how these are used. + +const c = @import("c.zig").c; + +// must be in sync with GLFW C constants in modifier group, search for "@defgroup mods Modifier key flags" +/// A bitmask of all key modifiers +pub const Mods = packed struct(u8) { + shift: bool = false, + control: bool = false, + alt: bool = false, + super: bool = false, + caps_lock: bool = false, + num_lock: bool = false, + _padding: u2 = 0, + + inline fn verifyIntType(comptime IntType: type) void { + comptime { + switch (@import("shims.zig").typeInfo(IntType)) { + .int => {}, + else => @compileError("Int was not of int type"), + } + } + } + + pub inline fn toInt(self: Mods, comptime IntType: type) IntType { + verifyIntType(IntType); + return @as(IntType, @intCast(@as(u8, @bitCast(self)))); + } + + pub inline fn fromInt(flags: anytype) Mods { + verifyIntType(@TypeOf(flags)); + return @as(Mods, @bitCast(@as(u8, @intCast(flags)))); + } +}; + +/// Holds all GLFW mod values in their raw form. +pub const RawMods = struct { + /// If this bit is set one or more Shift keys were held down. + pub const shift = c.GLFW_MOD_SHIFT; + + /// If this bit is set one or more Control keys were held down. + pub const control = c.GLFW_MOD_CONTROL; + + /// If this bit is set one or more Alt keys were held down. + pub const alt = c.GLFW_MOD_ALT; + + /// If this bit is set one or more Super keys were held down. + pub const super = c.GLFW_MOD_SUPER; + + /// If this bit is set the Caps Lock key is enabled and the glfw.lock_key_mods input mode is set. + pub const caps_lock = c.GLFW_MOD_CAPS_LOCK; + + /// If this bit is set the Num Lock key is enabled and the glfw.lock_key_mods input mode is set. + pub const num_lock = c.GLFW_MOD_NUM_LOCK; +}; + +test "shift int to bitmask" { + const std = @import("std"); + + const int_mod = RawMods.shift; + const mod = Mods.fromInt(int_mod); + + try std.testing.expect(mod.shift == true); + try std.testing.expect(mod.control == false); + try std.testing.expect(mod.alt == false); + try std.testing.expect(mod.super == false); + try std.testing.expect(mod.caps_lock == false); + try std.testing.expect(mod.num_lock == false); +} + +test "shift int and alt to bitmask" { + const std = @import("std"); + + const int_mod = RawMods.shift | RawMods.alt; + const mod = Mods.fromInt(int_mod); + + try std.testing.expect(mod.shift == true); + try std.testing.expect(mod.control == false); + try std.testing.expect(mod.alt == true); + try std.testing.expect(mod.super == false); + try std.testing.expect(mod.caps_lock == false); + try std.testing.expect(mod.num_lock == false); +} + +test "super int to bitmask" { + const std = @import("std"); + + const int_mod = RawMods.super; + const mod = Mods.fromInt(int_mod); + + try std.testing.expect(mod.shift == false); + try std.testing.expect(mod.control == false); + try std.testing.expect(mod.alt == false); + try std.testing.expect(mod.super == true); + try std.testing.expect(mod.caps_lock == false); + try std.testing.expect(mod.num_lock == false); +} + +test "num lock int to bitmask" { + const std = @import("std"); + + const int_mod = RawMods.num_lock; + const mod = Mods.fromInt(int_mod); + + try std.testing.expect(mod.shift == false); + try std.testing.expect(mod.control == false); + try std.testing.expect(mod.alt == false); + try std.testing.expect(mod.super == false); + try std.testing.expect(mod.caps_lock == false); + try std.testing.expect(mod.num_lock == true); +} + +test "all int to bitmask" { + const std = @import("std"); + + const int_mod = RawMods.shift | RawMods.control | + RawMods.alt | RawMods.super | + RawMods.caps_lock | RawMods.num_lock; + const mod = Mods.fromInt(int_mod); + + try std.testing.expect(mod.shift == true); + try std.testing.expect(mod.control == true); + try std.testing.expect(mod.alt == true); + try std.testing.expect(mod.super == true); + try std.testing.expect(mod.caps_lock == true); + try std.testing.expect(mod.num_lock == true); +} + +test "shift bitmask to int" { + const std = @import("std"); + + const mod = Mods{ .shift = true }; + const int_mod = mod.toInt(c_int); + + try std.testing.expectEqual(int_mod, RawMods.shift); +} + +test "shift and alt bitmask to int" { + const std = @import("std"); + + const mod = Mods{ .shift = true, .alt = true }; + const int_mod = mod.toInt(c_int); + + try std.testing.expectEqual(int_mod, RawMods.shift | RawMods.alt); +} + +test "all bitmask to int" { + const std = @import("std"); + + const mod = Mods{ + .shift = true, + .control = true, + .alt = true, + .super = true, + .caps_lock = true, + .num_lock = true, + }; + const int_mod = mod.toInt(c_int); + + const expected = RawMods.shift | RawMods.control | + RawMods.alt | RawMods.super | + RawMods.caps_lock | RawMods.num_lock; + + try std.testing.expectEqual(int_mod, expected); +} diff --git a/pkg/glfw/mouse_button.zig b/pkg/glfw/mouse_button.zig new file mode 100644 index 000000000..847049f5e --- /dev/null +++ b/pkg/glfw/mouse_button.zig @@ -0,0 +1,23 @@ +const c = @import("c.zig").c; + +/// Mouse button IDs. +/// +/// See glfw.setMouseButtonCallback for how these are used. +pub const MouseButton = enum(c_int) { + // We use left/right/middle aliases here because those are more common and we cannot have + // duplicate values in a Zig enum. + left = c.GLFW_MOUSE_BUTTON_1, + right = c.GLFW_MOUSE_BUTTON_2, + middle = c.GLFW_MOUSE_BUTTON_3, + four = c.GLFW_MOUSE_BUTTON_4, + five = c.GLFW_MOUSE_BUTTON_5, + six = c.GLFW_MOUSE_BUTTON_6, + seven = c.GLFW_MOUSE_BUTTON_7, + eight = c.GLFW_MOUSE_BUTTON_8, +}; + +/// Not in the MouseButton enumeration as it is a duplicate value which is forbidden. +pub const last = MouseButton.eight; +pub const one = MouseButton.left; +pub const two = MouseButton.right; +pub const three = MouseButton.middle; diff --git a/pkg/glfw/native.zig b/pkg/glfw/native.zig new file mode 100644 index 000000000..6b8f1831a --- /dev/null +++ b/pkg/glfw/native.zig @@ -0,0 +1,393 @@ +//! Native access functions +const std = @import("std"); + +const Window = @import("Window.zig"); +const Monitor = @import("Monitor.zig"); + +const internal_debug = @import("internal_debug.zig"); + +pub const BackendOptions = struct { + win32: bool = false, + wgl: bool = false, + cocoa: bool = false, + nsgl: bool = false, + x11: bool = false, + glx: bool = false, + wayland: bool = false, + egl: bool = false, + osmesa: bool = false, +}; + +/// This function returns a type which allows provides an interface to access +/// the native handles based on backends selected. +/// +/// The available window API options are: +/// * win32 +/// * cocoa +/// * x11 +/// * wayland +/// +/// The available context API options are: +/// +/// * wgl +/// * nsgl +/// * glx +/// * egl +/// * osmesa +/// +/// The chosen backends must match those the library was compiled for. Failure to do so +/// will cause a link-time error. +pub fn Native(comptime options: BackendOptions) type { + const native = @cImport({ + // @cDefine("GLFW_INCLUDE_VULKAN", "1"); + @cDefine("GLFW_INCLUDE_NONE", "1"); + if (options.win32) @cDefine("GLFW_EXPOSE_NATIVE_WIN32", "1"); + if (options.wgl) @cDefine("GLFW_EXPOSE_NATIVE_WGL", "1"); + if (options.cocoa) @cDefine("GLFW_EXPOSE_NATIVE_COCOA", "1"); + if (options.nsgl) @cDefine("GLFW_EXPOSE_NATIVE_NGSL", "1"); + if (options.x11) @cDefine("GLFW_EXPOSE_NATIVE_X11", "1"); + if (options.glx) @cDefine("GLFW_EXPOSE_NATIVE_GLX", "1"); + if (options.wayland) @cDefine("GLFW_EXPOSE_NATIVE_WAYLAND", "1"); + if (options.egl) @cDefine("GLFW_EXPOSE_NATIVE_EGL", "1"); + if (options.osmesa) @cDefine("GLFW_EXPOSE_NATIVE_OSMESA", "1"); + @cDefine("__kernel_ptr_semantics", ""); + @cInclude("GLFW/glfw3.h"); + @cInclude("GLFW/glfw3native.h"); + }); + + return struct { + /// Returns the adapter device name of the specified monitor. + /// + /// return: The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) of the + /// specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWin32Adapter(monitor: Monitor) [*:0]const u8 { + internal_debug.assertInitialized(); + if (native.glfwGetWin32Adapter(@as(*native.GLFWmonitor, @ptrCast(monitor.handle)))) |adapter| return adapter; + // `glfwGetWin32Adapter` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the display device name of the specified monitor. + /// + /// return: The UTF-8 encoded display device name (for example `\\.\DISPLAY1\Monitor0`) + /// of the specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWin32Monitor(monitor: Monitor) [*:0]const u8 { + internal_debug.assertInitialized(); + if (native.glfwGetWin32Monitor(@as(*native.GLFWmonitor, @ptrCast(monitor.handle)))) |mon| return mon; + // `glfwGetWin32Monitor` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `HWND` of the specified window. + /// + /// The `HDC` associated with the window can be queried with the + /// [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + /// function. + /// ``` + /// const dc = std.os.windows.user32.GetDC(native.getWin32Window(window)); + /// ``` + /// This DC is private and does not need to be released. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWin32Window(window: Window) std.os.windows.HWND { + internal_debug.assertInitialized(); + if (native.glfwGetWin32Window(@as(*native.GLFWwindow, @ptrCast(window.handle)))) |win| + return @as(std.os.windows.HWND, @ptrCast(win)); + // `glfwGetWin32Window` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `HGLRC` of the specified window. + /// + /// The `HDC` associated with the window can be queried with the + /// [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + /// function. + /// ``` + /// const dc = std.os.windows.user32.GetDC(native.getWin32Window(window)); + /// ``` + /// This DC is private and does not need to be released. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext + /// null is returned in the event of an error. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWGLContext(window: Window) ?std.os.windows.HGLRC { + internal_debug.assertInitialized(); + if (native.glfwGetWGLContext(@as(*native.GLFWwindow, @ptrCast(window.handle)))) |context| return context; + return null; + } + + /// Returns the `CGDirectDisplayID` of the specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getCocoaMonitor(monitor: Monitor) u32 { + internal_debug.assertInitialized(); + const mon = native.glfwGetCocoaMonitor(@as(*native.GLFWmonitor, @ptrCast(monitor.handle))); + if (mon != native.kCGNullDirectDisplay) return mon; + // `glfwGetCocoaMonitor` returns `kCGNullDirectDisplay` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `NSWindow` of the specified window. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getCocoaWindow(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + return native.glfwGetCocoaWindow(@as(*native.GLFWwindow, @ptrCast(window.handle))); + } + + /// Returns the `NSWindow` of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getNSGLContext(window: Window) u32 { + internal_debug.assertInitialized(); + return native.glfwGetNSGLContext(@as(*native.GLFWwindow, @ptrCast(window.handle))); + } + + /// Returns the `Display` used by GLFW. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getX11Display() *anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetX11Display()) |display| return @as(*anyopaque, @ptrCast(display)); + // `glfwGetX11Display` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `RRCrtc` of the specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getX11Adapter(monitor: Monitor) u32 { + internal_debug.assertInitialized(); + const adapter = native.glfwGetX11Adapter(@as(*native.GLFWMonitor, @ptrCast(monitor.handle))); + if (adapter != 0) return adapter; + // `glfwGetX11Adapter` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `RROutput` of the specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getX11Monitor(monitor: Monitor) u32 { + internal_debug.assertInitialized(); + const mon = native.glfwGetX11Monitor(@as(*native.GLFWmonitor, @ptrCast(monitor.handle))); + if (mon != 0) return mon; + // `glfwGetX11Monitor` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `Window` of the specified window. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getX11Window(window: Window) u32 { + internal_debug.assertInitialized(); + const win = native.glfwGetX11Window(@as(*native.GLFWwindow, @ptrCast(window.handle))); + if (win != 0) return @as(u32, @intCast(win)); + // `glfwGetX11Window` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Sets the current primary selection to the specified string. + /// + /// Possible errors include glfw.ErrorCode.PlatformError. + /// + /// The specified string is copied before this function returns. + /// + /// thread_safety: This function must only be called from the main thread. + pub fn setX11SelectionString(string: [*:0]const u8) void { + internal_debug.assertInitialized(); + native.glfwSetX11SelectionString(string); + } + + /// Returns the contents of the current primary selection as a string. + /// + /// Possible errors include glfw.ErrorCode.PlatformError. + /// Returns null in the event of an error. + /// + /// The returned string is allocated and freed by GLFW. You should not free it + /// yourself. It is valid until the next call to getX11SelectionString or + /// setX11SelectionString, or until the library is terminated. + /// + /// thread_safety: This function must only be called from the main thread. + pub fn getX11SelectionString() ?[*:0]const u8 { + internal_debug.assertInitialized(); + if (native.glfwGetX11SelectionString()) |str| return str; + return null; + } + + /// Returns the `GLXContext` of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext. + /// Returns null in the event of an error. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getGLXContext(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetGLXContext(@as(*native.GLFWwindow, @ptrCast(window.handle)))) |context| return @as(*anyopaque, @ptrCast(context)); + return null; + } + + /// Returns the `GLXWindow` of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext. + /// Returns null in the event of an error. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getGLXWindow(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + const win = native.glfwGetGLXWindow(@as(*native.GLFWwindow, @ptrCast(window.handle))); + if (win != 0) return @as(*anyopaque, @ptrCast(win)); + return null; + } + + /// Returns the `*wl_display` used by GLFW. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWaylandDisplay() *anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetWaylandDisplay()) |display| return @as(*anyopaque, @ptrCast(display)); + // `glfwGetWaylandDisplay` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `*wl_output` of the specified monitor. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWaylandMonitor(monitor: Monitor) *anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetWaylandMonitor(@as(*native.GLFWmonitor, @ptrCast(monitor.handle)))) |mon| return @as(*anyopaque, @ptrCast(mon)); + // `glfwGetWaylandMonitor` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `*wl_surface` of the specified window. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getWaylandWindow(window: Window) *anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetWaylandWindow(@as(*native.GLFWwindow, @ptrCast(window.handle)))) |win| return @as(*anyopaque, @ptrCast(win)); + // `glfwGetWaylandWindow` returns `null` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `EGLDisplay` used by GLFW. + /// + /// remark: Because EGL is initialized on demand, this function will return `EGL_NO_DISPLAY` + /// until the first context has been created via EGL. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getEGLDisplay() *anyopaque { + internal_debug.assertInitialized(); + const display = native.glfwGetEGLDisplay(); + if (display != native.EGL_NO_DISPLAY) return @as(*anyopaque, @ptrCast(display)); + // `glfwGetEGLDisplay` returns `EGL_NO_DISPLAY` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; + } + + /// Returns the `EGLContext` of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext. + /// Returns null in the event of an error. + /// + /// thread_safety This function may be called from any thread. Access is not synchronized. + pub fn getEGLContext(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + const context = native.glfwGetEGLContext(@as(*native.GLFWwindow, @ptrCast(window.handle))); + if (context != native.EGL_NO_CONTEXT) return @as(*anyopaque, @ptrCast(context)); + return null; + } + + /// Returns the `EGLSurface` of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NotInitalized and glfw.ErrorCode.NoWindowContext. + /// + /// thread_safety This function may be called from any thread. Access is not synchronized. + pub fn getEGLSurface(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + const surface = native.glfwGetEGLSurface(@as(*native.GLFWwindow, @ptrCast(window.handle))); + if (surface != native.EGL_NO_SURFACE) return @as(*anyopaque, @ptrCast(surface)); + return null; + } + + pub const OSMesaColorBuffer = struct { + width: c_int, + height: c_int, + format: c_int, + buffer: *anyopaque, + }; + + /// Retrieves the color buffer associated with the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext and glfw.ErrorCode.PlatformError. + /// Returns null in the event of an error. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getOSMesaColorBuffer(window: Window) ?OSMesaColorBuffer { + internal_debug.assertInitialized(); + var buf: OSMesaColorBuffer = undefined; + if (native.glfwGetOSMesaColorBuffer( + @as(*native.GLFWwindow, @ptrCast(window.handle)), + &buf.width, + &buf.height, + &buf.format, + &buf.buffer, + ) == native.GLFW_TRUE) return buf; + return null; + } + + pub const OSMesaDepthBuffer = struct { + width: c_int, + height: c_int, + bytes_per_value: c_int, + buffer: *anyopaque, + }; + + /// Retrieves the depth buffer associated with the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext and glfw.ErrorCode.PlatformError. + /// Returns null in the event of an error. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getOSMesaDepthBuffer(window: Window) ?OSMesaDepthBuffer { + internal_debug.assertInitialized(); + var buf: OSMesaDepthBuffer = undefined; + if (native.glfwGetOSMesaDepthBuffer( + @as(*native.GLFWwindow, @ptrCast(window.handle)), + &buf.width, + &buf.height, + &buf.bytes_per_value, + &buf.buffer, + ) == native.GLFW_TRUE) return buf; + return null; + } + + /// Returns the 'OSMesaContext' of the specified window. + /// + /// Possible errors include glfw.ErrorCode.NoWindowContext. + /// + /// thread_safety: This function may be called from any thread. Access is not synchronized. + pub fn getOSMesaContext(window: Window) ?*anyopaque { + internal_debug.assertInitialized(); + if (native.glfwGetOSMesaContext(@as(*native.GLFWwindow, @ptrCast(window.handle)))) |context| return @as(*anyopaque, @ptrCast(context)); + return null; + } + }; +} diff --git a/pkg/glfw/opengl.zig b/pkg/glfw/opengl.zig new file mode 100644 index 000000000..de99582c2 --- /dev/null +++ b/pkg/glfw/opengl.zig @@ -0,0 +1,256 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const c = @import("c.zig").c; +const Window = @import("Window.zig"); + +const internal_debug = @import("internal_debug.zig"); + +/// Makes the context of the specified window current for the calling thread. +/// +/// This function makes the OpenGL or OpenGL ES context of the specified window current on the +/// calling thread. A context must only be made current on a single thread at a time and each +/// thread can have only a single current context at a time. +/// +/// When moving a context between threads, you must make it non-current on the old thread before +/// making it current on the new one. +/// +/// By default, making a context non-current implicitly forces a pipeline flush. On machines that +/// support `GL_KHR_context_flush_control`, you can control whether a context performs this flush +/// by setting the glfw.context_release_behavior hint. +/// +/// The specified window must have an OpenGL or OpenGL ES context. Specifying a window without a +/// context will generate glfw.ErrorCode.NoWindowContext. +/// +/// @param[in] window The window whose context to make current, or null to +/// detach the current context. +/// +/// Possible errors include glfw.ErrorCode.NoWindowContext and glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: context_current, glfwGetCurrentContext +pub inline fn makeContextCurrent(window: ?Window) void { + internal_debug.assertInitialized(); + if (window) |w| c.glfwMakeContextCurrent(w.handle) else c.glfwMakeContextCurrent(null); +} + +/// Returns the window whose context is current on the calling thread. +/// +/// This function returns the window whose OpenGL or OpenGL ES context is current on the calling +/// thread. +/// +/// Returns he window whose context is current, or null if no window's context is current. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: context_current, glfwMakeContextCurrent +pub inline fn getCurrentContext() ?Window { + internal_debug.assertInitialized(); + if (c.glfwGetCurrentContext()) |handle| return Window.from(handle); + return null; +} + +/// Sets the swap interval for the current context. +/// +/// This function sets the swap interval for the current OpenGL or OpenGL ES context, i.e. the +/// number of screen updates to wait from the time glfw.SwapBuffers was called before swapping the +/// buffers and returning. This is sometimes called _vertical synchronization_, _vertical retrace +/// synchronization_ or just _vsync_. +/// +/// A context that supports either of the `WGL_EXT_swap_control_tear` and `GLX_EXT_swap_control_tear` +/// extensions also accepts _negative_ swap intervals, which allows the driver to swap immediately +/// even if a frame arrives a little bit late. You can check for these extensions with glfw.extensionSupported. +/// +/// A context must be current on the calling thread. Calling this function without a current context +/// will cause glfw.ErrorCode.NoCurrentContext. +/// +/// This function does not apply to Vulkan. If you are rendering with Vulkan, see the present mode +/// of your swapchain instead. +/// +/// @param[in] interval The minimum number of screen updates to wait for until the buffers are +/// swapped by glfw.swapBuffers. +/// +/// Possible errors include glfw.ErrorCode.NoCurrentContext and glfw.ErrorCode.PlatformError. +/// +/// This function is not called during context creation, leaving the swap interval set to whatever +/// is the default for that API. This is done because some swap interval extensions used by +/// GLFW do not allow the swap interval to be reset to zero once it has been set to a non-zero +/// value. +/// +/// Some GPU drivers do not honor the requested swap interval, either because of a user setting +/// that overrides the application's request or due to bugs in the driver. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: buffer_swap, glfwSwapBuffers +pub inline fn swapInterval(interval: i32) void { + internal_debug.assertInitialized(); + c.glfwSwapInterval(@as(c_int, @intCast(interval))); +} + +/// Returns whether the specified extension is available. +/// +/// This function returns whether the specified API extension (see context_glext) is supported by +/// the current OpenGL or OpenGL ES context. It searches both for client API extension and context +/// creation API extensions. +/// +/// A context must be current on the calling thread. Calling this function without a current +/// context will cause glfw.ErrorCode.NoCurrentContext. +/// +/// As this functions retrieves and searches one or more extension strings each call, it is +/// recommended that you cache its results if it is going to be used frequently. The extension +/// strings will not change during the lifetime of a context, so there is no danger in doing this. +/// +/// This function does not apply to Vulkan. If you are using Vulkan, see glfw.getRequiredInstanceExtensions, +/// `vkEnumerateInstanceExtensionProperties` and `vkEnumerateDeviceExtensionProperties` instead. +/// +/// @param[in] extension The ASCII encoded name of the extension. +/// @return `true` if the extension is available, or `false` otherwise. +/// +/// Possible errors include glfw.ErrorCode.NoCurrentContext, glfw.ErrorCode.InvalidValue +/// and glfw.ErrorCode.PlatformError. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: context_glext, glfw.getProcAddress +pub inline fn extensionSupported(extension: [:0]const u8) bool { + internal_debug.assertInitialized(); + + std.debug.assert(extension.len != 0); + std.debug.assert(extension[0] != 0); + + return c.glfwExtensionSupported(extension.ptr) == c.GLFW_TRUE; +} + +/// Client API function pointer type. +/// +/// Generic function pointer used for returning client API function pointers. +/// +/// see also: context_glext, glfwGetProcAddress +pub const GLProc = *const fn () callconv(if (builtin.os.tag == .windows and builtin.cpu.arch == .x86) .Stdcall else .C) void; + +/// Returns the address of the specified function for the current context. +/// +/// This function returns the address of the specified OpenGL or OpenGL ES core or extension +/// function (see context_glext), if it is supported by the current context. +/// +/// A context must be current on the calling thread. Calling this function without a current +/// context will cause glfw.ErrorCode.NoCurrentContext. +/// +/// This function does not apply to Vulkan. If you are rendering with Vulkan, see glfw.getInstanceProcAddress, +/// `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` instead. +/// +/// @param[in] procname The ASCII encoded name of the function. +/// @return The address of the function, or null if an error occurred. +/// +/// To maintain ABI compatability with the C glfwGetProcAddress, as it is commonly passed into +/// libraries expecting that exact ABI, this function does not return an error. Instead, if +/// glfw.ErrorCode.NotInitialized, glfw.ErrorCode.NoCurrentContext, or glfw.ErrorCode.PlatformError +/// would occur this function will panic. You should ensure a valid OpenGL context exists and the +/// GLFW is initialized before calling this function. +/// +/// The address of a given function is not guaranteed to be the same between contexts. +/// +/// This function may return a non-null address despite the associated version or extension +/// not being available. Always check the context version or extension string first. +/// +/// @pointer_lifetime The returned function pointer is valid until the context is destroyed or the +/// library is terminated. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: context_glext, glfwExtensionSupported +pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc { + internal_debug.assertInitialized(); + if (c.glfwGetProcAddress(proc_name)) |proc_address| return @ptrCast(proc_address); + return null; +} + +test "makeContextCurrent" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + glfw.makeContextCurrent(window); +} + +test "getCurrentContext" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const current_context = glfw.getCurrentContext(); + std.debug.assert(current_context == null); +} + +test "swapInterval" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + glfw.makeContextCurrent(window); + glfw.swapInterval(1); +} + +test "getProcAddress" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + glfw.makeContextCurrent(window); + _ = glfw.getProcAddress("foobar"); +} + +test "extensionSupported" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse { + std.log.warn("failed to create window: {?s}", .{glfw.getErrorString()}); + return error.SkipZigTest; // note: we don't exit(1) here because our CI can't open windows + }; + defer window.destroy(); + + glfw.makeContextCurrent(window); + _ = glfw.extensionSupported("foobar"); +} diff --git a/pkg/glfw/shims.zig b/pkg/glfw/shims.zig new file mode 100644 index 000000000..c731bb910 --- /dev/null +++ b/pkg/glfw/shims.zig @@ -0,0 +1,84 @@ +// Zig 0.14.0-dev changed the names of all 'std.builtin.Type' fields. +const old_std_builtin_type_field_names = @hasField(@import("std").builtin.Type, "Type"); + +pub const std = struct { + pub const builtin = struct { + pub const Type = if (old_std_builtin_type_field_names) union(enum) { + type: void, + void: void, + bool: void, + noreturn: void, + int: Int, + float: Float, + pointer: Pointer, + array: Array, + @"struct": Struct, + comptime_float: void, + comptime_int: void, + undefined: void, + null: void, + optional: Optional, + error_union: ErrorUnion, + error_set: ErrorSet, + @"enum": Enum, + @"union": Union, + @"fn": Fn, + @"opaque": Opaque, + frame: Frame, + @"anyframe": AnyFrame, + vector: Vector, + enum_literal: void, + + pub const Int = @import("std").builtin.Type.Int; + pub const Float = @import("std").builtin.Type.Float; + pub const Pointer = @import("std").builtin.Type.Pointer; + pub const Array = @import("std").builtin.Type.Array; + pub const ContainerLayout = @import("std").builtin.Type.ContainerLayout; + pub const StructField = @import("std").builtin.Type.StructField; + pub const Struct = @import("std").builtin.Type.Struct; + pub const Optional = @import("std").builtin.Type.Optional; + pub const ErrorUnion = @import("std").builtin.Type.ErrorUnion; + pub const Error = @import("std").builtin.Type.Error; + pub const ErrorSet = @import("std").builtin.Type.ErrorSet; + pub const EnumField = @import("std").builtin.Type.EnumField; + pub const Enum = @import("std").builtin.Type.Enum; + pub const UnionField = @import("std").builtin.Type.UnionField; + pub const Union = @import("std").builtin.Type.Union; + pub const Fn = @import("std").builtin.Type.Fn; + pub const Opaque = @import("std").builtin.Type.Opaque; + pub const Frame = @import("std").builtin.Type.Frame; + pub const AnyFrame = @import("std").builtin.Type.AnyFrame; + pub const Vector = @import("std").builtin.Type.Vector; + pub const Declaration = @import("std").builtin.Type.Declaration; + } else @import("std").builtin.Type; + }; +}; + +pub fn typeInfo(comptime T: type) std.builtin.Type { + return if (old_std_builtin_type_field_names) switch (@typeInfo(T)) { + .Type => .type, + .Void => .void, + .Bool => .bool, + .NoReturn => .noreturn, + .Int => |x| .{ .int = x }, + .Float => |x| .{ .float = x }, + .Pointer => |x| .{ .pointer = x }, + .Array => |x| .{ .array = x }, + .Struct => |x| .{ .@"struct" = x }, + .ComptimeFloat => .comptime_float, + .ComptimeInt => .comptime_int, + .Undefined => .undefined, + .Null => .null, + .Optional => |x| .{ .optional = x }, + .ErrorUnion => |x| .{ .error_union = x }, + .ErrorSet => |x| .{ .error_set = x }, + .Enum => |x| .{ .@"enum" = x }, + .Union => |x| .{ .@"union" = x }, + .Fn => |x| .{ .@"fn" = x }, + .Opaque => |x| .{ .@"opaque" = x }, + .Frame => |x| .{ .frame = x }, + .AnyFrame => .@"anyframe", + .Vector => |x| .{ .vector = x }, + .EnumLiteral => .enum_literal, + } else @typeInfo(T); +} diff --git a/pkg/glfw/time.zig b/pkg/glfw/time.zig new file mode 100644 index 000000000..c3432b105 --- /dev/null +++ b/pkg/glfw/time.zig @@ -0,0 +1,153 @@ +const std = @import("std"); + +const c = @import("c.zig").c; + +const internal_debug = @import("internal_debug.zig"); + +/// Returns the GLFW time. +/// +/// This function returns the current GLFW time, in seconds. Unless the time +/// has been set using @ref glfwSetTime it measures time elapsed since GLFW was +/// initialized. +/// +/// This function and @ref glfwSetTime are helper functions on top of glfw.getTimerFrequency +/// and glfw.getTimerValue. +/// +/// The resolution of the timer is system dependent, but is usually on the order +/// of a few micro- or nanoseconds. It uses the highest-resolution monotonic +/// time source on each supported operating system. +/// +/// @return The current time, in seconds, or zero if an +/// error occurred. +/// +/// @thread_safety This function may be called from any thread. Reading and +/// writing of the internal base time is not atomic, so it needs to be +/// externally synchronized with calls to @ref glfwSetTime. +/// +/// see also: time +pub inline fn getTime() f64 { + internal_debug.assertInitialized(); + const time = c.glfwGetTime(); + if (time != 0) return time; + // `glfwGetTime` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; +} + +/// Sets the GLFW time. +/// +/// This function sets the current GLFW time, in seconds. The value must be a positive finite +/// number less than or equal to 18446744073.0, which is approximately 584.5 years. +/// +/// This function and @ref glfwGetTime are helper functions on top of glfw.getTimerFrequency and +/// glfw.getTimerValue. +/// +/// @param[in] time The new value, in seconds. +/// +/// Possible errors include glfw.ErrorCode.InvalidValue. +/// +/// The upper limit of GLFW time is calculated as `floor((2^64 - 1) / 10^9)` and is due to +/// implementations storing nanoseconds in 64 bits. The limit may be increased in the future. +/// +/// @thread_safety This function may be called from any thread. Reading and writing of the internal +/// base time is not atomic, so it needs to be externally synchronized with calls to glfw.getTime. +/// +/// see also: time +pub inline fn setTime(time: f64) void { + internal_debug.assertInitialized(); + + std.debug.assert(!std.math.isNan(time)); + std.debug.assert(time >= 0); + // assert time is lteq to largest number of seconds representable by u64 with nanosecond precision + std.debug.assert(time <= max_time: { + const @"2^64" = std.math.maxInt(u64); + break :max_time @divTrunc(@"2^64", std.time.ns_per_s); + }); + + c.glfwSetTime(time); +} + +/// Returns the current value of the raw timer. +/// +/// This function returns the current value of the raw timer, measured in `1/frequency` seconds. To +/// get the frequency, call glfw.getTimerFrequency. +/// +/// @return The value of the timer, or zero if an error occurred. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: time, glfw.getTimerFrequency +pub inline fn getTimerValue() u64 { + internal_debug.assertInitialized(); + const value = c.glfwGetTimerValue(); + if (value != 0) return value; + // `glfwGetTimerValue` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; +} + +/// Returns the frequency, in Hz, of the raw timer. +/// +/// This function returns the frequency, in Hz, of the raw timer. +/// +/// @return The frequency of the timer, in Hz, or zero if an error occurred. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: time, glfw.getTimerValue +pub inline fn getTimerFrequency() u64 { + internal_debug.assertInitialized(); + const frequency = c.glfwGetTimerFrequency(); + if (frequency != 0) return frequency; + // `glfwGetTimerFrequency` returns `0` only for errors + // but the only potential error is unreachable (NotInitialized) + unreachable; +} + +test "getTime" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = getTime(); +} + +test "setTime" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.setTime(1234); +} + +test "getTimerValue" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.getTimerValue(); +} + +test "getTimerFrequency" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.getTimerFrequency(); +} diff --git a/pkg/glfw/version.zig b/pkg/glfw/version.zig new file mode 100644 index 000000000..e83a4d708 --- /dev/null +++ b/pkg/glfw/version.zig @@ -0,0 +1,18 @@ +//! GLFW version info + +const c = @import("c.zig").c; + +/// The major version number of the GLFW library. +/// +/// This is incremented when the API is changed in non-compatible ways. +pub const major = c.GLFW_VERSION_MAJOR; + +/// The minor version number of the GLFW library. +/// +/// This is incremented when features are added to the API but it remains backward-compatible. +pub const minor = c.GLFW_VERSION_MINOR; + +/// The revision number of the GLFW library. +/// +/// This is incremented when a bug fix release is made that does not contain any API changes. +pub const revision = c.GLFW_VERSION_REVISION; diff --git a/pkg/glfw/vulkan.zig b/pkg/glfw/vulkan.zig new file mode 100644 index 000000000..6c6021d02 --- /dev/null +++ b/pkg/glfw/vulkan.zig @@ -0,0 +1,290 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const c = @import("c.zig").c; +const Window = @import("Window.zig"); + +const internal_debug = @import("internal_debug.zig"); + +/// Sets the desired Vulkan `vkGetInstanceProcAddr` function. +/// +/// This function sets the `vkGetInstanceProcAddr` function that GLFW will use for all +/// Vulkan related entry point queries. +/// +/// This feature is mostly useful on macOS, if your copy of the Vulkan loader is in +/// a location where GLFW cannot find it through dynamic loading, or if you are still +/// using the static library version of the loader. +/// +/// If set to `NULL`, GLFW will try to load the Vulkan loader dynamically by its standard +/// name and get this function from there. This is the default behavior. +/// +/// The standard name of the loader is `vulkan-1.dll` on Windows, `libvulkan.so.1` on +/// Linux and other Unix-like systems and `libvulkan.1.dylib` on macOS. If your code is +/// also loading it via these names then you probably don't need to use this function. +/// +/// The function address you set is never reset by GLFW, but it only takes effect during +/// initialization. Once GLFW has been initialized, any updates will be ignored until the +/// library is terminated and initialized again. +/// +/// remark: This function may be called before glfw.Init. +/// +/// thread_safety: This function must only be called from the main thread. +pub fn initVulkanLoader(loader_function: ?VKGetInstanceProcAddr) void { + c.glfwInitVulkanLoader(loader_function orelse null); +} + +pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.C) ?VKProc; + +/// Returns whether the Vulkan loader and an ICD have been found. +/// +/// This function returns whether the Vulkan loader and any minimally functional ICD have been +/// found. +/// +/// The availability of a Vulkan loader and even an ICD does not by itself guarantee that surface +/// creation or even instance creation is possible. Call glfw.getRequiredInstanceExtensions +/// to check whether the extensions necessary for Vulkan surface creation are available and +/// glfw.getPhysicalDevicePresentationSupport to check whether a queue family of a physical device +/// supports image presentation. +/// +/// @return `true` if Vulkan is minimally available, or `false` otherwise. +/// +/// @thread_safety This function may be called from any thread. +pub inline fn vulkanSupported() bool { + internal_debug.assertInitialized(); + const supported = c.glfwVulkanSupported(); + return supported == c.GLFW_TRUE; +} + +/// Returns the Vulkan instance extensions required by GLFW. +/// +/// This function returns an array of names of Vulkan instance extensions required by GLFW for +/// creating Vulkan surfaces for GLFW windows. If successful, the list will always contain +/// `VK_KHR_surface`, so if you don't require any additional extensions you can pass this list +/// directly to the `VkInstanceCreateInfo` struct. +/// +/// If Vulkan is not available on the machine, this function returns null and generates a +/// glfw.ErrorCode.APIUnavailable error. Call glfw.vulkanSupported to check whether Vulkan is at +/// least minimally available. +/// +/// If Vulkan is available but no set of extensions allowing window surface creation was found, +/// this function returns null. You may still use Vulkan for off-screen rendering and compute work. +/// +/// Possible errors include glfw.ErrorCode.APIUnavailable. +/// Returns null in the event of an error. +/// +/// Additional extensions may be required by future versions of GLFW. You should check if any +/// extensions you wish to enable are already in the returned array, as it is an error to specify +/// an extension more than once in the `VkInstanceCreateInfo` struct. +/// +/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it +/// yourself. It is guaranteed to be valid only until the library is terminated. +/// +/// @thread_safety This function may be called from any thread. +/// +/// see also: vulkan_ext, glfwCreateWindowSurface +pub inline fn getRequiredInstanceExtensions() ?[][*:0]const u8 { + internal_debug.assertInitialized(); + var count: u32 = 0; + if (c.glfwGetRequiredInstanceExtensions(&count)) |extensions| return @as([*][*:0]const u8, @ptrCast(extensions))[0..count]; + return null; +} + +/// Vulkan API function pointer type. +/// +/// Generic function pointer used for returning Vulkan API function pointers. +/// +/// see also: vulkan_proc, glfw.getInstanceProcAddress +pub const VKProc = *const fn () callconv(if (builtin.os.tag == .windows and builtin.cpu.arch == .x86) .Stdcall else .C) void; + +/// Returns the address of the specified Vulkan instance function. +/// +/// This function returns the address of the specified Vulkan core or extension function for the +/// specified instance. If instance is set to null it can return any function exported from the +/// Vulkan loader, including at least the following functions: +/// +/// - `vkEnumerateInstanceExtensionProperties` +/// - `vkEnumerateInstanceLayerProperties` +/// - `vkCreateInstance` +/// - `vkGetInstanceProcAddr` +/// +/// If Vulkan is not available on the machine, this function returns null and generates a +/// glfw.ErrorCode.APIUnavailable error. Call glfw.vulkanSupported to check whether Vulkan is at +/// least minimally available. +/// +/// This function is equivalent to calling `vkGetInstanceProcAddr` with a platform-specific query +/// of the Vulkan loader as a fallback. +/// +/// @param[in] instance The Vulkan instance to query, or null to retrieve functions related to +/// instance creation. +/// @param[in] procname The ASCII encoded name of the function. +/// @return The address of the function, or null if an error occurred. +/// +/// To maintain ABI compatability with the C glfwGetInstanceProcAddress, as it is commonly passed +/// into libraries expecting that exact ABI, this function does not return an error. Instead, if +/// glfw.ErrorCode.NotInitialized or glfw.ErrorCode.APIUnavailable would occur this function will panic. +/// You may check glfw.vulkanSupported prior to invoking this function. +/// +/// @pointer_lifetime The returned function pointer is valid until the library is terminated. +/// +/// @thread_safety This function may be called from any thread. +pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc { + internal_debug.assertInitialized(); + if (c.glfwGetInstanceProcAddress(if (vk_instance) |v| @as(c.VkInstance, @ptrCast(v)) else null, proc_name)) |proc_address| return proc_address; + return null; +} + +/// Returns whether the specified queue family can present images. +/// +/// This function returns whether the specified queue family of the specified physical device +/// supports presentation to the platform GLFW was built for. +/// +/// If Vulkan or the required window surface creation instance extensions are not available on the +/// machine, or if the specified instance was not created with the required extensions, this +/// function returns `GLFW_FALSE` and generates a glfw.ErrorCode.APIUnavailable error. Call +/// glfw.vulkanSupported to check whether Vulkan is at least minimally available and +/// glfw.getRequiredInstanceExtensions to check what instance extensions are required. +/// +/// @param[in] instance The instance that the physical device belongs to. +/// @param[in] device The physical device that the queue family belongs to. +/// @param[in] queuefamily The index of the queue family to query. +/// @return `true` if the queue family supports presentation, or `false` otherwise. +/// +/// Possible errors include glfw.ErrorCode.APIUnavailable and glfw.ErrorCode.PlatformError. +/// Returns false in the event of an error. +/// +/// macos: This function currently always returns `true`, as the `VK_MVK_macos_surface` and +/// 'VK_EXT_metal_surface' extension does not provide a `vkGetPhysicalDevice*PresentationSupport` type function. +/// +/// @thread_safety This function may be called from any thread. For synchronization details of +/// Vulkan objects, see the Vulkan specification. +/// +/// see also: vulkan_present +pub inline fn getPhysicalDevicePresentationSupport( + vk_instance: *anyopaque, + vk_physical_device: *anyopaque, + queue_family: u32, +) bool { + internal_debug.assertInitialized(); + return c.glfwGetPhysicalDevicePresentationSupport( + @as(c.VkInstance, @ptrCast(vk_instance)), + @as(c.VkPhysicalDevice, @ptrCast(vk_physical_device)), + queue_family, + ) == c.GLFW_TRUE; +} + +/// Creates a Vulkan surface for the specified window. +/// +/// This function creates a Vulkan surface for the specified window. +/// +/// If the Vulkan loader or at least one minimally functional ICD were not found, this function +/// returns `VK_ERROR_INITIALIZATION_FAILED` and generates a glfw.ErrorCode.APIUnavailable error. Call +/// glfw.vulkanSupported to check whether Vulkan is at least minimally available. +/// +/// If the required window surface creation instance extensions are not available or if the +/// specified instance was not created with these extensions enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` +/// and generates a glfw.ErrorCode.APIUnavailable error. Call glfw.getRequiredInstanceExtensions to +/// check what instance extensions are required. +/// +/// The window surface cannot be shared with another API so the window must have been created with +/// the client api hint set to `GLFW_NO_API` otherwise it generates a glfw.ErrorCode.InvalidValue error +/// and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. +/// +/// The window surface must be destroyed before the specified Vulkan instance. It is the +/// responsibility of the caller to destroy the window surface. GLFW does not destroy it for you. +/// Call `vkDestroySurfaceKHR` to destroy the surface. +/// +/// @param[in] vk_instance The Vulkan instance to create the surface in. +/// @param[in] window The window to create the surface for. +/// @param[in] vk_allocation_callbacks The allocator to use, or null to use the default +/// allocator. +/// @param[out] surface Where to store the handle of the surface. This is set +/// to `VK_NULL_HANDLE` if an error occurred. +/// @return `VkResult` type, `VK_SUCCESS` if successful, or a Vulkan error code if an +/// error occurred. +/// +/// Possible errors include glfw.ErrorCode.APIUnavailable, glfw.ErrorCode.PlatformError and glfw.ErrorCode.InvalidValue +/// Returns a bool indicating success. +/// +/// If an error occurs before the creation call is made, GLFW returns the Vulkan error code most +/// appropriate for the error. Appropriate use of glfw.vulkanSupported and glfw.getRequiredInstanceExtensions +/// should eliminate almost all occurrences of these errors. +/// +/// macos: GLFW prefers the `VK_EXT_metal_surface` extension, with the `VK_MVK_macos_surface` +/// extension as a fallback. The name of the selected extension, if any, is included in the array +/// returned by glfw.getRequiredInstanceExtensions. +/// +/// macos: This function currently only supports the `VK_MVK_macos_surface` extension from MoltenVK. +/// +/// macos: This function creates and sets a `CAMetalLayer` instance for the window content view, +/// which is required for MoltenVK to function. +/// +/// x11: By default GLFW prefers the `VK_KHR_xcb_surface` extension, with the `VK_KHR_xlib_surface` +/// extension as a fallback. You can make `VK_KHR_xlib_surface` the preferred extension by setting +/// glfw.InitHints.x11_xcb_vulkan_surface. The name of the selected extension, if any, is included +/// in the array returned by glfw.getRequiredInstanceExtensions. +/// +/// @thread_safety This function may be called from any thread. For synchronization details of +/// Vulkan objects, see the Vulkan specification. +/// +/// see also: vulkan_surface, glfw.getRequiredInstanceExtensions +pub inline fn createWindowSurface(vk_instance: anytype, window: Window, vk_allocation_callbacks: anytype, vk_surface_khr: anytype) i32 { + internal_debug.assertInitialized(); + // zig-vulkan uses enums to represent opaque pointers: + // pub const Instance = enum(usize) { null_handle = 0, _ }; + const instance: c.VkInstance = switch (@import("shims.zig").typeInfo(@TypeOf(vk_instance))) { + .@"enum" => @as(c.VkInstance, @ptrFromInt(@intFromEnum(vk_instance))), + else => @as(c.VkInstance, @ptrCast(vk_instance)), + }; + + return c.glfwCreateWindowSurface( + instance, + window.handle, + if (vk_allocation_callbacks == null) null else @as(*const c.VkAllocationCallbacks, @ptrCast(@alignCast(vk_allocation_callbacks))), + @as(*c.VkSurfaceKHR, @ptrCast(@alignCast(vk_surface_khr))), + ); +} + +test "vulkanSupported" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.vulkanSupported(); +} + +test "getRequiredInstanceExtensions" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + _ = glfw.getRequiredInstanceExtensions(); +} + +test "getInstanceProcAddress" { + const glfw = @import("main.zig"); + defer glfw.clearError(); // clear any error we generate + if (!glfw.init(.{})) { + std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()}); + std.process.exit(1); + } + defer glfw.terminate(); + + // syntax check only, we don't have a real vulkan instance and so this function would panic. + _ = glfw.getInstanceProcAddress; +} + +test "syntax" { + // Best we can do for these two functions in terms of testing in lieu of an actual Vulkan + // context. + _ = getPhysicalDevicePresentationSupport; + _ = createWindowSurface; + _ = initVulkanLoader; +} diff --git a/pkg/glfw/wayland-headers/wayland-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-client-protocol-code.h new file mode 100755 index 000000000..68b0ecbc1 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-client-protocol-code.h @@ -0,0 +1,524 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2010-2011 Intel Corporation + * Copyright © 2012-2013 Collabora, Ltd. + * + * 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 (including the + * next paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_buffer_interface; +extern const struct wl_interface wl_callback_interface; +extern const struct wl_interface wl_data_device_interface; +extern const struct wl_interface wl_data_offer_interface; +extern const struct wl_interface wl_data_source_interface; +extern const struct wl_interface wl_keyboard_interface; +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_pointer_interface; +extern const struct wl_interface wl_region_interface; +extern const struct wl_interface wl_registry_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_shm_pool_interface; +extern const struct wl_interface wl_subsurface_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface wl_touch_interface; + +static const struct wl_interface *wayland_types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_callback_interface, + &wl_registry_interface, + &wl_surface_interface, + &wl_region_interface, + &wl_buffer_interface, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_shm_pool_interface, + NULL, + NULL, + &wl_data_source_interface, + &wl_surface_interface, + &wl_surface_interface, + NULL, + &wl_data_source_interface, + NULL, + &wl_data_offer_interface, + NULL, + &wl_surface_interface, + NULL, + NULL, + &wl_data_offer_interface, + &wl_data_offer_interface, + &wl_data_source_interface, + &wl_data_device_interface, + &wl_seat_interface, + &wl_shell_surface_interface, + &wl_surface_interface, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_surface_interface, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &wl_surface_interface, + NULL, + NULL, + NULL, + &wl_output_interface, + &wl_buffer_interface, + NULL, + NULL, + &wl_callback_interface, + &wl_region_interface, + &wl_region_interface, + &wl_output_interface, + &wl_output_interface, + &wl_pointer_interface, + &wl_keyboard_interface, + &wl_touch_interface, + NULL, + &wl_surface_interface, + NULL, + NULL, + NULL, + &wl_surface_interface, + NULL, + NULL, + NULL, + &wl_surface_interface, + NULL, + &wl_surface_interface, + NULL, + NULL, + &wl_surface_interface, + NULL, + NULL, + &wl_surface_interface, + NULL, + NULL, + NULL, + &wl_subsurface_interface, + &wl_surface_interface, + &wl_surface_interface, + &wl_surface_interface, + &wl_surface_interface, +}; + +static const struct wl_message wl_display_requests[] = { + { "sync", "n", wayland_types + 8 }, + { "get_registry", "n", wayland_types + 9 }, +}; + +static const struct wl_message wl_display_events[] = { + { "error", "ous", wayland_types + 0 }, + { "delete_id", "u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_display_interface = { + "wl_display", 1, + 2, wl_display_requests, + 2, wl_display_events, +}; + +static const struct wl_message wl_registry_requests[] = { + { "bind", "usun", wayland_types + 0 }, +}; + +static const struct wl_message wl_registry_events[] = { + { "global", "usu", wayland_types + 0 }, + { "global_remove", "u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_registry_interface = { + "wl_registry", 1, + 1, wl_registry_requests, + 2, wl_registry_events, +}; + +static const struct wl_message wl_callback_events[] = { + { "done", "u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_callback_interface = { + "wl_callback", 1, + 0, NULL, + 1, wl_callback_events, +}; + +static const struct wl_message wl_compositor_requests[] = { + { "create_surface", "n", wayland_types + 10 }, + { "create_region", "n", wayland_types + 11 }, +}; + +WL_PRIVATE const struct wl_interface wl_compositor_interface = { + "wl_compositor", 6, + 2, wl_compositor_requests, + 0, NULL, +}; + +static const struct wl_message wl_shm_pool_requests[] = { + { "create_buffer", "niiiiu", wayland_types + 12 }, + { "destroy", "", wayland_types + 0 }, + { "resize", "i", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_shm_pool_interface = { + "wl_shm_pool", 1, + 3, wl_shm_pool_requests, + 0, NULL, +}; + +static const struct wl_message wl_shm_requests[] = { + { "create_pool", "nhi", wayland_types + 18 }, +}; + +static const struct wl_message wl_shm_events[] = { + { "format", "u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_shm_interface = { + "wl_shm", 1, + 1, wl_shm_requests, + 1, wl_shm_events, +}; + +static const struct wl_message wl_buffer_requests[] = { + { "destroy", "", wayland_types + 0 }, +}; + +static const struct wl_message wl_buffer_events[] = { + { "release", "", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_buffer_interface = { + "wl_buffer", 1, + 1, wl_buffer_requests, + 1, wl_buffer_events, +}; + +static const struct wl_message wl_data_offer_requests[] = { + { "accept", "u?s", wayland_types + 0 }, + { "receive", "sh", wayland_types + 0 }, + { "destroy", "", wayland_types + 0 }, + { "finish", "3", wayland_types + 0 }, + { "set_actions", "3uu", wayland_types + 0 }, +}; + +static const struct wl_message wl_data_offer_events[] = { + { "offer", "s", wayland_types + 0 }, + { "source_actions", "3u", wayland_types + 0 }, + { "action", "3u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_data_offer_interface = { + "wl_data_offer", 3, + 5, wl_data_offer_requests, + 3, wl_data_offer_events, +}; + +static const struct wl_message wl_data_source_requests[] = { + { "offer", "s", wayland_types + 0 }, + { "destroy", "", wayland_types + 0 }, + { "set_actions", "3u", wayland_types + 0 }, +}; + +static const struct wl_message wl_data_source_events[] = { + { "target", "?s", wayland_types + 0 }, + { "send", "sh", wayland_types + 0 }, + { "cancelled", "", wayland_types + 0 }, + { "dnd_drop_performed", "3", wayland_types + 0 }, + { "dnd_finished", "3", wayland_types + 0 }, + { "action", "3u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_data_source_interface = { + "wl_data_source", 3, + 3, wl_data_source_requests, + 6, wl_data_source_events, +}; + +static const struct wl_message wl_data_device_requests[] = { + { "start_drag", "?oo?ou", wayland_types + 21 }, + { "set_selection", "?ou", wayland_types + 25 }, + { "release", "2", wayland_types + 0 }, +}; + +static const struct wl_message wl_data_device_events[] = { + { "data_offer", "n", wayland_types + 27 }, + { "enter", "uoff?o", wayland_types + 28 }, + { "leave", "", wayland_types + 0 }, + { "motion", "uff", wayland_types + 0 }, + { "drop", "", wayland_types + 0 }, + { "selection", "?o", wayland_types + 33 }, +}; + +WL_PRIVATE const struct wl_interface wl_data_device_interface = { + "wl_data_device", 3, + 3, wl_data_device_requests, + 6, wl_data_device_events, +}; + +static const struct wl_message wl_data_device_manager_requests[] = { + { "create_data_source", "n", wayland_types + 34 }, + { "get_data_device", "no", wayland_types + 35 }, +}; + +WL_PRIVATE const struct wl_interface wl_data_device_manager_interface = { + "wl_data_device_manager", 3, + 2, wl_data_device_manager_requests, + 0, NULL, +}; + +static const struct wl_message wl_shell_requests[] = { + { "get_shell_surface", "no", wayland_types + 37 }, +}; + +WL_PRIVATE const struct wl_interface wl_shell_interface = { + "wl_shell", 1, + 1, wl_shell_requests, + 0, NULL, +}; + +static const struct wl_message wl_shell_surface_requests[] = { + { "pong", "u", wayland_types + 0 }, + { "move", "ou", wayland_types + 39 }, + { "resize", "ouu", wayland_types + 41 }, + { "set_toplevel", "", wayland_types + 0 }, + { "set_transient", "oiiu", wayland_types + 44 }, + { "set_fullscreen", "uu?o", wayland_types + 48 }, + { "set_popup", "ouoiiu", wayland_types + 51 }, + { "set_maximized", "?o", wayland_types + 57 }, + { "set_title", "s", wayland_types + 0 }, + { "set_class", "s", wayland_types + 0 }, +}; + +static const struct wl_message wl_shell_surface_events[] = { + { "ping", "u", wayland_types + 0 }, + { "configure", "uii", wayland_types + 0 }, + { "popup_done", "", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_shell_surface_interface = { + "wl_shell_surface", 1, + 10, wl_shell_surface_requests, + 3, wl_shell_surface_events, +}; + +static const struct wl_message wl_surface_requests[] = { + { "destroy", "", wayland_types + 0 }, + { "attach", "?oii", wayland_types + 58 }, + { "damage", "iiii", wayland_types + 0 }, + { "frame", "n", wayland_types + 61 }, + { "set_opaque_region", "?o", wayland_types + 62 }, + { "set_input_region", "?o", wayland_types + 63 }, + { "commit", "", wayland_types + 0 }, + { "set_buffer_transform", "2i", wayland_types + 0 }, + { "set_buffer_scale", "3i", wayland_types + 0 }, + { "damage_buffer", "4iiii", wayland_types + 0 }, + { "offset", "5ii", wayland_types + 0 }, +}; + +static const struct wl_message wl_surface_events[] = { + { "enter", "o", wayland_types + 64 }, + { "leave", "o", wayland_types + 65 }, + { "preferred_buffer_scale", "6i", wayland_types + 0 }, + { "preferred_buffer_transform", "6u", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_surface_interface = { + "wl_surface", 6, + 11, wl_surface_requests, + 4, wl_surface_events, +}; + +static const struct wl_message wl_seat_requests[] = { + { "get_pointer", "n", wayland_types + 66 }, + { "get_keyboard", "n", wayland_types + 67 }, + { "get_touch", "n", wayland_types + 68 }, + { "release", "5", wayland_types + 0 }, +}; + +static const struct wl_message wl_seat_events[] = { + { "capabilities", "u", wayland_types + 0 }, + { "name", "2s", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_seat_interface = { + "wl_seat", 9, + 4, wl_seat_requests, + 2, wl_seat_events, +}; + +static const struct wl_message wl_pointer_requests[] = { + { "set_cursor", "u?oii", wayland_types + 69 }, + { "release", "3", wayland_types + 0 }, +}; + +static const struct wl_message wl_pointer_events[] = { + { "enter", "uoff", wayland_types + 73 }, + { "leave", "uo", wayland_types + 77 }, + { "motion", "uff", wayland_types + 0 }, + { "button", "uuuu", wayland_types + 0 }, + { "axis", "uuf", wayland_types + 0 }, + { "frame", "5", wayland_types + 0 }, + { "axis_source", "5u", wayland_types + 0 }, + { "axis_stop", "5uu", wayland_types + 0 }, + { "axis_discrete", "5ui", wayland_types + 0 }, + { "axis_value120", "8ui", wayland_types + 0 }, + { "axis_relative_direction", "9uu", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_pointer_interface = { + "wl_pointer", 9, + 2, wl_pointer_requests, + 11, wl_pointer_events, +}; + +static const struct wl_message wl_keyboard_requests[] = { + { "release", "3", wayland_types + 0 }, +}; + +static const struct wl_message wl_keyboard_events[] = { + { "keymap", "uhu", wayland_types + 0 }, + { "enter", "uoa", wayland_types + 79 }, + { "leave", "uo", wayland_types + 82 }, + { "key", "uuuu", wayland_types + 0 }, + { "modifiers", "uuuuu", wayland_types + 0 }, + { "repeat_info", "4ii", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_keyboard_interface = { + "wl_keyboard", 9, + 1, wl_keyboard_requests, + 6, wl_keyboard_events, +}; + +static const struct wl_message wl_touch_requests[] = { + { "release", "3", wayland_types + 0 }, +}; + +static const struct wl_message wl_touch_events[] = { + { "down", "uuoiff", wayland_types + 84 }, + { "up", "uui", wayland_types + 0 }, + { "motion", "uiff", wayland_types + 0 }, + { "frame", "", wayland_types + 0 }, + { "cancel", "", wayland_types + 0 }, + { "shape", "6iff", wayland_types + 0 }, + { "orientation", "6if", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_touch_interface = { + "wl_touch", 9, + 1, wl_touch_requests, + 7, wl_touch_events, +}; + +static const struct wl_message wl_output_requests[] = { + { "release", "3", wayland_types + 0 }, +}; + +static const struct wl_message wl_output_events[] = { + { "geometry", "iiiiissi", wayland_types + 0 }, + { "mode", "uiii", wayland_types + 0 }, + { "done", "2", wayland_types + 0 }, + { "scale", "2i", wayland_types + 0 }, + { "name", "4s", wayland_types + 0 }, + { "description", "4s", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_output_interface = { + "wl_output", 4, + 1, wl_output_requests, + 6, wl_output_events, +}; + +static const struct wl_message wl_region_requests[] = { + { "destroy", "", wayland_types + 0 }, + { "add", "iiii", wayland_types + 0 }, + { "subtract", "iiii", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_region_interface = { + "wl_region", 1, + 3, wl_region_requests, + 0, NULL, +}; + +static const struct wl_message wl_subcompositor_requests[] = { + { "destroy", "", wayland_types + 0 }, + { "get_subsurface", "noo", wayland_types + 90 }, +}; + +WL_PRIVATE const struct wl_interface wl_subcompositor_interface = { + "wl_subcompositor", 1, + 2, wl_subcompositor_requests, + 0, NULL, +}; + +static const struct wl_message wl_subsurface_requests[] = { + { "destroy", "", wayland_types + 0 }, + { "set_position", "ii", wayland_types + 0 }, + { "place_above", "o", wayland_types + 93 }, + { "place_below", "o", wayland_types + 94 }, + { "set_sync", "", wayland_types + 0 }, + { "set_desync", "", wayland_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_subsurface_interface = { + "wl_subsurface", 1, + 6, wl_subsurface_requests, + 0, NULL, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol-code.h new file mode 100755 index 000000000..251a915ba --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol-code.h @@ -0,0 +1,68 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_idle_inhibitor_v1_interface; + +static const struct wl_interface *idle_inhibit_unstable_v1_types[] = { + &zwp_idle_inhibitor_v1_interface, + &wl_surface_interface, +}; + +static const struct wl_message zwp_idle_inhibit_manager_v1_requests[] = { + { "destroy", "", idle_inhibit_unstable_v1_types + 0 }, + { "create_inhibitor", "no", idle_inhibit_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { + "zwp_idle_inhibit_manager_v1", 1, + 2, zwp_idle_inhibit_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_idle_inhibitor_v1_requests[] = { + { "destroy", "", idle_inhibit_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwp_idle_inhibitor_v1_interface = { + "zwp_idle_inhibitor_v1", 1, + 1, zwp_idle_inhibitor_v1_requests, + 0, NULL, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol.h b/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol.h new file mode 100755 index 000000000..1a8de3be6 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-idle-inhibit-unstable-v1-client-protocol.h @@ -0,0 +1,232 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_idle_inhibit_unstable_v1 The idle_inhibit_unstable_v1 protocol + * @section page_ifaces_idle_inhibit_unstable_v1 Interfaces + * - @subpage page_iface_zwp_idle_inhibit_manager_v1 - control behavior when display idles + * - @subpage page_iface_zwp_idle_inhibitor_v1 - context object for inhibiting idle behavior + * @section page_copyright_idle_inhibit_unstable_v1 Copyright + *
+ *
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct wl_surface; +struct zwp_idle_inhibit_manager_v1; +struct zwp_idle_inhibitor_v1; + +#ifndef ZWP_IDLE_INHIBIT_MANAGER_V1_INTERFACE +#define ZWP_IDLE_INHIBIT_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwp_idle_inhibit_manager_v1 zwp_idle_inhibit_manager_v1 + * @section page_iface_zwp_idle_inhibit_manager_v1_desc Description + * + * This interface permits inhibiting the idle behavior such as screen + * blanking, locking, and screensaving. The client binds the idle manager + * globally, then creates idle-inhibitor objects for each surface. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * @section page_iface_zwp_idle_inhibit_manager_v1_api API + * See @ref iface_zwp_idle_inhibit_manager_v1. + */ +/** + * @defgroup iface_zwp_idle_inhibit_manager_v1 The zwp_idle_inhibit_manager_v1 interface + * + * This interface permits inhibiting the idle behavior such as screen + * blanking, locking, and screensaving. The client binds the idle manager + * globally, then creates idle-inhibitor objects for each surface. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + */ +extern const struct wl_interface zwp_idle_inhibit_manager_v1_interface; +#endif +#ifndef ZWP_IDLE_INHIBITOR_V1_INTERFACE +#define ZWP_IDLE_INHIBITOR_V1_INTERFACE +/** + * @page page_iface_zwp_idle_inhibitor_v1 zwp_idle_inhibitor_v1 + * @section page_iface_zwp_idle_inhibitor_v1_desc Description + * + * An idle inhibitor prevents the output that the associated surface is + * visible on from being set to a state where it is not visually usable due + * to lack of user interaction (e.g. blanked, dimmed, locked, set to power + * save, etc.) Any screensaver processes are also blocked from displaying. + * + * If the surface is destroyed, unmapped, becomes occluded, loses + * visibility, or otherwise becomes not visually relevant for the user, the + * idle inhibitor will not be honored by the compositor; if the surface + * subsequently regains visibility the inhibitor takes effect once again. + * Likewise, the inhibitor isn't honored if the system was already idled at + * the time the inhibitor was established, although if the system later + * de-idles and re-idles the inhibitor will take effect. + * @section page_iface_zwp_idle_inhibitor_v1_api API + * See @ref iface_zwp_idle_inhibitor_v1. + */ +/** + * @defgroup iface_zwp_idle_inhibitor_v1 The zwp_idle_inhibitor_v1 interface + * + * An idle inhibitor prevents the output that the associated surface is + * visible on from being set to a state where it is not visually usable due + * to lack of user interaction (e.g. blanked, dimmed, locked, set to power + * save, etc.) Any screensaver processes are also blocked from displaying. + * + * If the surface is destroyed, unmapped, becomes occluded, loses + * visibility, or otherwise becomes not visually relevant for the user, the + * idle inhibitor will not be honored by the compositor; if the surface + * subsequently regains visibility the inhibitor takes effect once again. + * Likewise, the inhibitor isn't honored if the system was already idled at + * the time the inhibitor was established, although if the system later + * de-idles and re-idles the inhibitor will take effect. + */ +extern const struct wl_interface zwp_idle_inhibitor_v1_interface; +#endif + +#define ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY 0 +#define ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR 1 + + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + */ +#define ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + */ +#define ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR_SINCE_VERSION 1 + +/** @ingroup iface_zwp_idle_inhibit_manager_v1 */ +static inline void +zwp_idle_inhibit_manager_v1_set_user_data(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_idle_inhibit_manager_v1, user_data); +} + +/** @ingroup iface_zwp_idle_inhibit_manager_v1 */ +static inline void * +zwp_idle_inhibit_manager_v1_get_user_data(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_idle_inhibit_manager_v1); +} + +static inline uint32_t +zwp_idle_inhibit_manager_v1_get_version(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibit_manager_v1); +} + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + * + * Destroy the inhibit manager. + */ +static inline void +zwp_idle_inhibit_manager_v1_destroy(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_idle_inhibit_manager_v1, + ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibit_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + * + * Create a new inhibitor object associated with the given surface. + */ +static inline struct zwp_idle_inhibitor_v1 * +zwp_idle_inhibit_manager_v1_create_inhibitor(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_idle_inhibit_manager_v1, + ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR, &zwp_idle_inhibitor_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibit_manager_v1), 0, NULL, surface); + + return (struct zwp_idle_inhibitor_v1 *) id; +} + +#define ZWP_IDLE_INHIBITOR_V1_DESTROY 0 + + +/** + * @ingroup iface_zwp_idle_inhibitor_v1 + */ +#define ZWP_IDLE_INHIBITOR_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwp_idle_inhibitor_v1 */ +static inline void +zwp_idle_inhibitor_v1_set_user_data(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_idle_inhibitor_v1, user_data); +} + +/** @ingroup iface_zwp_idle_inhibitor_v1 */ +static inline void * +zwp_idle_inhibitor_v1_get_user_data(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_idle_inhibitor_v1); +} + +static inline uint32_t +zwp_idle_inhibitor_v1_get_version(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibitor_v1); +} + +/** + * @ingroup iface_zwp_idle_inhibitor_v1 + * + * Remove the inhibitor effect from the associated wl_surface. + */ +static inline void +zwp_idle_inhibitor_v1_destroy(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_idle_inhibitor_v1, + ZWP_IDLE_INHIBITOR_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibitor_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol-code.h new file mode 100755 index 000000000..9aa943fa6 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol-code.h @@ -0,0 +1,108 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2014 Jonas Ådahl + * Copyright © 2015 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_pointer_interface; +extern const struct wl_interface wl_region_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_confined_pointer_v1_interface; +extern const struct wl_interface zwp_locked_pointer_v1_interface; + +static const struct wl_interface *pointer_constraints_unstable_v1_types[] = { + NULL, + NULL, + &zwp_locked_pointer_v1_interface, + &wl_surface_interface, + &wl_pointer_interface, + &wl_region_interface, + NULL, + &zwp_confined_pointer_v1_interface, + &wl_surface_interface, + &wl_pointer_interface, + &wl_region_interface, + NULL, + &wl_region_interface, + &wl_region_interface, +}; + +static const struct wl_message zwp_pointer_constraints_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2 }, + { "confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7 }, +}; + +WL_PRIVATE const struct wl_interface zwp_pointer_constraints_v1_interface = { + "zwp_pointer_constraints_v1", 1, + 3, zwp_pointer_constraints_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_locked_pointer_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_cursor_position_hint", "ff", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 12 }, +}; + +static const struct wl_message zwp_locked_pointer_v1_events[] = { + { "locked", "", pointer_constraints_unstable_v1_types + 0 }, + { "unlocked", "", pointer_constraints_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwp_locked_pointer_v1_interface = { + "zwp_locked_pointer_v1", 1, + 3, zwp_locked_pointer_v1_requests, + 2, zwp_locked_pointer_v1_events, +}; + +static const struct wl_message zwp_confined_pointer_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 13 }, +}; + +static const struct wl_message zwp_confined_pointer_v1_events[] = { + { "confined", "", pointer_constraints_unstable_v1_types + 0 }, + { "unconfined", "", pointer_constraints_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwp_confined_pointer_v1_interface = { + "zwp_confined_pointer_v1", 1, + 2, zwp_confined_pointer_v1_requests, + 2, zwp_confined_pointer_v1_events, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol.h b/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol.h new file mode 100755 index 000000000..dd84a3f8b --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-pointer-constraints-unstable-v1-client-protocol.h @@ -0,0 +1,667 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_pointer_constraints_unstable_v1 The pointer_constraints_unstable_v1 protocol + * protocol for constraining pointer motions + * + * @section page_desc_pointer_constraints_unstable_v1 Description + * + * This protocol specifies a set of interfaces used for adding constraints to + * the motion of a pointer. Possible constraints include confining pointer + * motions to a given region, or locking it to its current position. + * + * In order to constrain the pointer, a client must first bind the global + * interface "wp_pointer_constraints" which, if a compositor supports pointer + * constraints, is exposed by the registry. Using the bound global object, the + * client uses the request that corresponds to the type of constraint it wants + * to make. See wp_pointer_constraints for more details. + * + * Warning! The protocol described in this file is experimental and backward + * incompatible changes may be made. Backward compatible changes may be added + * together with the corresponding interface version bump. Backward + * incompatible changes are done by bumping the version number in the protocol + * and interface names and resetting the interface version. Once the protocol + * is to be declared stable, the 'z' prefix and the version number in the + * protocol and interface names are removed and the interface version number is + * reset. + * + * @section page_ifaces_pointer_constraints_unstable_v1 Interfaces + * - @subpage page_iface_zwp_pointer_constraints_v1 - constrain the movement of a pointer + * - @subpage page_iface_zwp_locked_pointer_v1 - receive relative pointer motion events + * - @subpage page_iface_zwp_confined_pointer_v1 - confined pointer object + * @section page_copyright_pointer_constraints_unstable_v1 Copyright + *
+ *
+ * Copyright © 2014      Jonas Ådahl
+ * Copyright © 2015      Red Hat Inc.
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct wl_pointer; +struct wl_region; +struct wl_surface; +struct zwp_confined_pointer_v1; +struct zwp_locked_pointer_v1; +struct zwp_pointer_constraints_v1; + +#ifndef ZWP_POINTER_CONSTRAINTS_V1_INTERFACE +#define ZWP_POINTER_CONSTRAINTS_V1_INTERFACE +/** + * @page page_iface_zwp_pointer_constraints_v1 zwp_pointer_constraints_v1 + * @section page_iface_zwp_pointer_constraints_v1_desc Description + * + * The global interface exposing pointer constraining functionality. It + * exposes two requests: lock_pointer for locking the pointer to its + * position, and confine_pointer for locking the pointer to a region. + * + * The lock_pointer and confine_pointer requests create the objects + * wp_locked_pointer and wp_confined_pointer respectively, and the client can + * use these objects to interact with the lock. + * + * For any surface, only one lock or confinement may be active across all + * wl_pointer objects of the same seat. If a lock or confinement is requested + * when another lock or confinement is active or requested on the same surface + * and with any of the wl_pointer objects of the same seat, an + * 'already_constrained' error will be raised. + * @section page_iface_zwp_pointer_constraints_v1_api API + * See @ref iface_zwp_pointer_constraints_v1. + */ +/** + * @defgroup iface_zwp_pointer_constraints_v1 The zwp_pointer_constraints_v1 interface + * + * The global interface exposing pointer constraining functionality. It + * exposes two requests: lock_pointer for locking the pointer to its + * position, and confine_pointer for locking the pointer to a region. + * + * The lock_pointer and confine_pointer requests create the objects + * wp_locked_pointer and wp_confined_pointer respectively, and the client can + * use these objects to interact with the lock. + * + * For any surface, only one lock or confinement may be active across all + * wl_pointer objects of the same seat. If a lock or confinement is requested + * when another lock or confinement is active or requested on the same surface + * and with any of the wl_pointer objects of the same seat, an + * 'already_constrained' error will be raised. + */ +extern const struct wl_interface zwp_pointer_constraints_v1_interface; +#endif +#ifndef ZWP_LOCKED_POINTER_V1_INTERFACE +#define ZWP_LOCKED_POINTER_V1_INTERFACE +/** + * @page page_iface_zwp_locked_pointer_v1 zwp_locked_pointer_v1 + * @section page_iface_zwp_locked_pointer_v1_desc Description + * + * The wp_locked_pointer interface represents a locked pointer state. + * + * While the lock of this object is active, the wl_pointer objects of the + * associated seat will not emit any wl_pointer.motion events. + * + * This object will send the event 'locked' when the lock is activated. + * Whenever the lock is activated, it is guaranteed that the locked surface + * will already have received pointer focus and that the pointer will be + * within the region passed to the request creating this object. + * + * To unlock the pointer, send the destroy request. This will also destroy + * the wp_locked_pointer object. + * + * If the compositor decides to unlock the pointer the unlocked event is + * sent. See wp_locked_pointer.unlock for details. + * + * When unlocking, the compositor may warp the cursor position to the set + * cursor position hint. If it does, it will not result in any relative + * motion events emitted via wp_relative_pointer. + * + * If the surface the lock was requested on is destroyed and the lock is not + * yet activated, the wp_locked_pointer object is now defunct and must be + * destroyed. + * @section page_iface_zwp_locked_pointer_v1_api API + * See @ref iface_zwp_locked_pointer_v1. + */ +/** + * @defgroup iface_zwp_locked_pointer_v1 The zwp_locked_pointer_v1 interface + * + * The wp_locked_pointer interface represents a locked pointer state. + * + * While the lock of this object is active, the wl_pointer objects of the + * associated seat will not emit any wl_pointer.motion events. + * + * This object will send the event 'locked' when the lock is activated. + * Whenever the lock is activated, it is guaranteed that the locked surface + * will already have received pointer focus and that the pointer will be + * within the region passed to the request creating this object. + * + * To unlock the pointer, send the destroy request. This will also destroy + * the wp_locked_pointer object. + * + * If the compositor decides to unlock the pointer the unlocked event is + * sent. See wp_locked_pointer.unlock for details. + * + * When unlocking, the compositor may warp the cursor position to the set + * cursor position hint. If it does, it will not result in any relative + * motion events emitted via wp_relative_pointer. + * + * If the surface the lock was requested on is destroyed and the lock is not + * yet activated, the wp_locked_pointer object is now defunct and must be + * destroyed. + */ +extern const struct wl_interface zwp_locked_pointer_v1_interface; +#endif +#ifndef ZWP_CONFINED_POINTER_V1_INTERFACE +#define ZWP_CONFINED_POINTER_V1_INTERFACE +/** + * @page page_iface_zwp_confined_pointer_v1 zwp_confined_pointer_v1 + * @section page_iface_zwp_confined_pointer_v1_desc Description + * + * The wp_confined_pointer interface represents a confined pointer state. + * + * This object will send the event 'confined' when the confinement is + * activated. Whenever the confinement is activated, it is guaranteed that + * the surface the pointer is confined to will already have received pointer + * focus and that the pointer will be within the region passed to the request + * creating this object. It is up to the compositor to decide whether this + * requires some user interaction and if the pointer will warp to within the + * passed region if outside. + * + * To unconfine the pointer, send the destroy request. This will also destroy + * the wp_confined_pointer object. + * + * If the compositor decides to unconfine the pointer the unconfined event is + * sent. The wp_confined_pointer object is at this point defunct and should + * be destroyed. + * @section page_iface_zwp_confined_pointer_v1_api API + * See @ref iface_zwp_confined_pointer_v1. + */ +/** + * @defgroup iface_zwp_confined_pointer_v1 The zwp_confined_pointer_v1 interface + * + * The wp_confined_pointer interface represents a confined pointer state. + * + * This object will send the event 'confined' when the confinement is + * activated. Whenever the confinement is activated, it is guaranteed that + * the surface the pointer is confined to will already have received pointer + * focus and that the pointer will be within the region passed to the request + * creating this object. It is up to the compositor to decide whether this + * requires some user interaction and if the pointer will warp to within the + * passed region if outside. + * + * To unconfine the pointer, send the destroy request. This will also destroy + * the wp_confined_pointer object. + * + * If the compositor decides to unconfine the pointer the unconfined event is + * sent. The wp_confined_pointer object is at this point defunct and should + * be destroyed. + */ +extern const struct wl_interface zwp_confined_pointer_v1_interface; +#endif + +#ifndef ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM +#define ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * wp_pointer_constraints error values + * + * These errors can be emitted in response to wp_pointer_constraints + * requests. + */ +enum zwp_pointer_constraints_v1_error { + /** + * pointer constraint already requested on that surface + */ + ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED = 1, +}; +#endif /* ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM */ + +#ifndef ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM +#define ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * constraint lifetime + * + * These values represent different lifetime semantics. They are passed + * as arguments to the factory requests to specify how the constraint + * lifetimes should be managed. + */ +enum zwp_pointer_constraints_v1_lifetime { + /** + * the pointer constraint is defunct once deactivated + * + * A oneshot pointer constraint will never reactivate once it has + * been deactivated. See the corresponding deactivation event + * (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) + * for details. + */ + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT = 1, + /** + * the pointer constraint may reactivate + * + * A persistent pointer constraint may again reactivate once it + * has been deactivated. See the corresponding deactivation event + * (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) + * for details. + */ + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT = 2, +}; +#endif /* ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM */ + +#define ZWP_POINTER_CONSTRAINTS_V1_DESTROY 0 +#define ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER 1 +#define ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER 2 + + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER_SINCE_VERSION 1 + +/** @ingroup iface_zwp_pointer_constraints_v1 */ +static inline void +zwp_pointer_constraints_v1_set_user_data(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_pointer_constraints_v1, user_data); +} + +/** @ingroup iface_zwp_pointer_constraints_v1 */ +static inline void * +zwp_pointer_constraints_v1_get_user_data(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_pointer_constraints_v1); +} + +static inline uint32_t +zwp_pointer_constraints_v1_get_version(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_pointer_constraints_v1); +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * Used by the client to notify the server that it will no longer use this + * pointer constraints object. + */ +static inline void +zwp_pointer_constraints_v1_destroy(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_pointer_constraints_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * The lock_pointer request lets the client request to disable movements of + * the virtual pointer (i.e. the cursor), effectively locking the pointer + * to a position. This request may not take effect immediately; in the + * future, when the compositor deems implementation-specific constraints + * are satisfied, the pointer lock will be activated and the compositor + * sends a locked event. + * + * The protocol provides no guarantee that the constraints are ever + * satisfied, and does not require the compositor to send an error if the + * constraints cannot ever be satisfied. It is thus possible to request a + * lock that will never activate. + * + * There may not be another pointer constraint of any kind requested or + * active on the surface for any of the wl_pointer objects of the seat of + * the passed pointer when requesting a lock. If there is, an error will be + * raised. See general pointer lock documentation for more details. + * + * The intersection of the region passed with this request and the input + * region of the surface is used to determine where the pointer must be + * in order for the lock to activate. It is up to the compositor whether to + * warp the pointer or require some kind of user interaction for the lock + * to activate. If the region is null the surface input region is used. + * + * A surface may receive pointer focus without the lock being activated. + * + * The request creates a new object wp_locked_pointer which is used to + * interact with the lock as well as receive updates about its state. See + * the the description of wp_locked_pointer for further information. + * + * Note that while a pointer is locked, the wl_pointer objects of the + * corresponding seat will not emit any wl_pointer.motion events, but + * relative motion events will still be emitted via wp_relative_pointer + * objects of the same seat. wl_pointer.axis and wl_pointer.button events + * are unaffected. + */ +static inline struct zwp_locked_pointer_v1 * +zwp_pointer_constraints_v1_lock_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER, &zwp_locked_pointer_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_pointer_constraints_v1), 0, NULL, surface, pointer, region, lifetime); + + return (struct zwp_locked_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * The confine_pointer request lets the client request to confine the + * pointer cursor to a given region. This request may not take effect + * immediately; in the future, when the compositor deems implementation- + * specific constraints are satisfied, the pointer confinement will be + * activated and the compositor sends a confined event. + * + * The intersection of the region passed with this request and the input + * region of the surface is used to determine where the pointer must be + * in order for the confinement to activate. It is up to the compositor + * whether to warp the pointer or require some kind of user interaction for + * the confinement to activate. If the region is null the surface input + * region is used. + * + * The request will create a new object wp_confined_pointer which is used + * to interact with the confinement as well as receive updates about its + * state. See the the description of wp_confined_pointer for further + * information. + */ +static inline struct zwp_confined_pointer_v1 * +zwp_pointer_constraints_v1_confine_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER, &zwp_confined_pointer_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_pointer_constraints_v1), 0, NULL, surface, pointer, region, lifetime); + + return (struct zwp_confined_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * @struct zwp_locked_pointer_v1_listener + */ +struct zwp_locked_pointer_v1_listener { + /** + * lock activation event + * + * Notification that the pointer lock of the seat's pointer is + * activated. + */ + void (*locked)(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1); + /** + * lock deactivation event + * + * Notification that the pointer lock of the seat's pointer is no + * longer active. If this is a oneshot pointer lock (see + * wp_pointer_constraints.lifetime) this object is now defunct and + * should be destroyed. If this is a persistent pointer lock (see + * wp_pointer_constraints.lifetime) this pointer lock may again + * reactivate in the future. + */ + void (*unlocked)(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1); +}; + +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +static inline int +zwp_locked_pointer_v1_add_listener(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, + const struct zwp_locked_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_locked_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_LOCKED_POINTER_V1_DESTROY 0 +#define ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT 1 +#define ZWP_LOCKED_POINTER_V1_SET_REGION 2 + +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_LOCKED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_UNLOCKED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_SET_REGION_SINCE_VERSION 1 + +/** @ingroup iface_zwp_locked_pointer_v1 */ +static inline void +zwp_locked_pointer_v1_set_user_data(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_locked_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_locked_pointer_v1 */ +static inline void * +zwp_locked_pointer_v1_get_user_data(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_locked_pointer_v1); +} + +static inline uint32_t +zwp_locked_pointer_v1_get_version(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_locked_pointer_v1); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Destroy the locked pointer object. If applicable, the compositor will + * unlock the pointer. + */ +static inline void +zwp_locked_pointer_v1_destroy(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_locked_pointer_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Set the cursor position hint relative to the top left corner of the + * surface. + * + * If the client is drawing its own cursor, it should update the position + * hint to the position of its own cursor. A compositor may use this + * information to warp the pointer upon unlock in order to avoid pointer + * jumps. + * + * The cursor position hint is double buffered. The new hint will only take + * effect when the associated surface gets it pending state applied. See + * wl_surface.commit for details. + */ +static inline void +zwp_locked_pointer_v1_set_cursor_position_hint(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_locked_pointer_v1), 0, surface_x, surface_y); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Set a new region used to lock the pointer. + * + * The new lock region is double-buffered. The new lock region will + * only take effect when the associated surface gets its pending state + * applied. See wl_surface.commit for details. + * + * For details about the lock region, see wp_locked_pointer. + */ +static inline void +zwp_locked_pointer_v1_set_region(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, struct wl_region *region) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_SET_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_locked_pointer_v1), 0, region); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * @struct zwp_confined_pointer_v1_listener + */ +struct zwp_confined_pointer_v1_listener { + /** + * pointer confined + * + * Notification that the pointer confinement of the seat's + * pointer is activated. + */ + void (*confined)(void *data, + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1); + /** + * pointer unconfined + * + * Notification that the pointer confinement of the seat's + * pointer is no longer active. If this is a oneshot pointer + * confinement (see wp_pointer_constraints.lifetime) this object is + * now defunct and should be destroyed. If this is a persistent + * pointer confinement (see wp_pointer_constraints.lifetime) this + * pointer confinement may again reactivate in the future. + */ + void (*unconfined)(void *data, + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1); +}; + +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +static inline int +zwp_confined_pointer_v1_add_listener(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, + const struct zwp_confined_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_confined_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_CONFINED_POINTER_V1_DESTROY 0 +#define ZWP_CONFINED_POINTER_V1_SET_REGION 1 + +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_CONFINED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_UNCONFINED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_SET_REGION_SINCE_VERSION 1 + +/** @ingroup iface_zwp_confined_pointer_v1 */ +static inline void +zwp_confined_pointer_v1_set_user_data(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_confined_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_confined_pointer_v1 */ +static inline void * +zwp_confined_pointer_v1_get_user_data(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_confined_pointer_v1); +} + +static inline uint32_t +zwp_confined_pointer_v1_get_version(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_confined_pointer_v1); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * + * Destroy the confined pointer object. If applicable, the compositor will + * unconfine the pointer. + */ +static inline void +zwp_confined_pointer_v1_destroy(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_confined_pointer_v1, + ZWP_CONFINED_POINTER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_confined_pointer_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * + * Set a new region used to confine the pointer. + * + * The new confine region is double-buffered. The new confine region will + * only take effect when the associated surface gets its pending state + * applied. See wl_surface.commit for details. + * + * If the confinement is active when the new confinement region is applied + * and the pointer ends up outside of newly applied region, the pointer may + * warped to a position within the new confinement region. If warped, a + * wl_pointer.motion event will be emitted, but no + * wp_relative_pointer.relative_motion event. + * + * The compositor may also, instead of using the new region, unconfine the + * pointer. + * + * For details about the confine region, see wp_confined_pointer. + */ +static inline void +zwp_confined_pointer_v1_set_region(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, struct wl_region *region) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_confined_pointer_v1, + ZWP_CONFINED_POINTER_V1_SET_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_confined_pointer_v1), 0, region); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol-code.h new file mode 100755 index 000000000..4707c7860 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol-code.h @@ -0,0 +1,79 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2014 Jonas Ådahl + * Copyright © 2015 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_pointer_interface; +extern const struct wl_interface zwp_relative_pointer_v1_interface; + +static const struct wl_interface *relative_pointer_unstable_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &zwp_relative_pointer_v1_interface, + &wl_pointer_interface, +}; + +static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = { + { "destroy", "", relative_pointer_unstable_v1_types + 0 }, + { "get_relative_pointer", "no", relative_pointer_unstable_v1_types + 6 }, +}; + +WL_PRIVATE const struct wl_interface zwp_relative_pointer_manager_v1_interface = { + "zwp_relative_pointer_manager_v1", 1, + 2, zwp_relative_pointer_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_relative_pointer_v1_requests[] = { + { "destroy", "", relative_pointer_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwp_relative_pointer_v1_events[] = { + { "relative_motion", "uuffff", relative_pointer_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwp_relative_pointer_v1_interface = { + "zwp_relative_pointer_v1", 1, + 1, zwp_relative_pointer_v1_requests, + 1, zwp_relative_pointer_v1_events, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol.h b/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol.h new file mode 100755 index 000000000..bbcb0e568 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-relative-pointer-unstable-v1-client-protocol.h @@ -0,0 +1,297 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_relative_pointer_unstable_v1 The relative_pointer_unstable_v1 protocol + * protocol for relative pointer motion events + * + * @section page_desc_relative_pointer_unstable_v1 Description + * + * This protocol specifies a set of interfaces used for making clients able to + * receive relative pointer events not obstructed by barriers (such as the + * monitor edge or other pointer barriers). + * + * To start receiving relative pointer events, a client must first bind the + * global interface "wp_relative_pointer_manager" which, if a compositor + * supports relative pointer motion events, is exposed by the registry. After + * having created the relative pointer manager proxy object, the client uses + * it to create the actual relative pointer object using the + * "get_relative_pointer" request given a wl_pointer. The relative pointer + * motion events will then, when applicable, be transmitted via the proxy of + * the newly created relative pointer object. See the documentation of the + * relative pointer interface for more details. + * + * Warning! The protocol described in this file is experimental and backward + * incompatible changes may be made. Backward compatible changes may be added + * together with the corresponding interface version bump. Backward + * incompatible changes are done by bumping the version number in the protocol + * and interface names and resetting the interface version. Once the protocol + * is to be declared stable, the 'z' prefix and the version number in the + * protocol and interface names are removed and the interface version number is + * reset. + * + * @section page_ifaces_relative_pointer_unstable_v1 Interfaces + * - @subpage page_iface_zwp_relative_pointer_manager_v1 - get relative pointer objects + * - @subpage page_iface_zwp_relative_pointer_v1 - relative pointer object + * @section page_copyright_relative_pointer_unstable_v1 Copyright + *
+ *
+ * Copyright © 2014      Jonas Ådahl
+ * Copyright © 2015      Red Hat Inc.
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct wl_pointer; +struct zwp_relative_pointer_manager_v1; +struct zwp_relative_pointer_v1; + +#ifndef ZWP_RELATIVE_POINTER_MANAGER_V1_INTERFACE +#define ZWP_RELATIVE_POINTER_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwp_relative_pointer_manager_v1 zwp_relative_pointer_manager_v1 + * @section page_iface_zwp_relative_pointer_manager_v1_desc Description + * + * A global interface used for getting the relative pointer object for a + * given pointer. + * @section page_iface_zwp_relative_pointer_manager_v1_api API + * See @ref iface_zwp_relative_pointer_manager_v1. + */ +/** + * @defgroup iface_zwp_relative_pointer_manager_v1 The zwp_relative_pointer_manager_v1 interface + * + * A global interface used for getting the relative pointer object for a + * given pointer. + */ +extern const struct wl_interface zwp_relative_pointer_manager_v1_interface; +#endif +#ifndef ZWP_RELATIVE_POINTER_V1_INTERFACE +#define ZWP_RELATIVE_POINTER_V1_INTERFACE +/** + * @page page_iface_zwp_relative_pointer_v1 zwp_relative_pointer_v1 + * @section page_iface_zwp_relative_pointer_v1_desc Description + * + * A wp_relative_pointer object is an extension to the wl_pointer interface + * used for emitting relative pointer events. It shares the same focus as + * wl_pointer objects of the same seat and will only emit events when it has + * focus. + * @section page_iface_zwp_relative_pointer_v1_api API + * See @ref iface_zwp_relative_pointer_v1. + */ +/** + * @defgroup iface_zwp_relative_pointer_v1 The zwp_relative_pointer_v1 interface + * + * A wp_relative_pointer object is an extension to the wl_pointer interface + * used for emitting relative pointer events. It shares the same focus as + * wl_pointer objects of the same seat and will only emit events when it has + * focus. + */ +extern const struct wl_interface zwp_relative_pointer_v1_interface; +#endif + +#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY 0 +#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER 1 + + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + */ +#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + */ +#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER_SINCE_VERSION 1 + +/** @ingroup iface_zwp_relative_pointer_manager_v1 */ +static inline void +zwp_relative_pointer_manager_v1_set_user_data(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_relative_pointer_manager_v1, user_data); +} + +/** @ingroup iface_zwp_relative_pointer_manager_v1 */ +static inline void * +zwp_relative_pointer_manager_v1_get_user_data(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_relative_pointer_manager_v1); +} + +static inline uint32_t +zwp_relative_pointer_manager_v1_get_version(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_manager_v1); +} + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + * + * Used by the client to notify the server that it will no longer use this + * relative pointer manager object. + */ +static inline void +zwp_relative_pointer_manager_v1_destroy(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_relative_pointer_manager_v1, + ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + * + * Create a relative pointer interface given a wl_pointer object. See the + * wp_relative_pointer interface for more details. + */ +static inline struct zwp_relative_pointer_v1 * +zwp_relative_pointer_manager_v1_get_relative_pointer(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1, struct wl_pointer *pointer) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_relative_pointer_manager_v1, + ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER, &zwp_relative_pointer_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_manager_v1), 0, NULL, pointer); + + return (struct zwp_relative_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_relative_pointer_v1 + * @struct zwp_relative_pointer_v1_listener + */ +struct zwp_relative_pointer_v1_listener { + /** + * relative pointer motion + * + * Relative x/y pointer motion from the pointer of the seat + * associated with this object. + * + * A relative motion is in the same dimension as regular wl_pointer + * motion events, except they do not represent an absolute + * position. For example, moving a pointer from (x, y) to (x', y') + * would have the equivalent relative motion (x' - x, y' - y). If a + * pointer motion caused the absolute pointer position to be + * clipped by for example the edge of the monitor, the relative + * motion is unaffected by the clipping and will represent the + * unclipped motion. + * + * This event also contains non-accelerated motion deltas. The + * non-accelerated delta is, when applicable, the regular pointer + * motion delta as it was before having applied motion acceleration + * and other transformations such as normalization. + * + * Note that the non-accelerated delta does not represent 'raw' + * events as they were read from some device. Pointer motion + * acceleration is device- and configuration-specific and + * non-accelerated deltas and accelerated deltas may have the same + * value on some devices. + * + * Relative motions are not coupled to wl_pointer.motion events, + * and can be sent in combination with such events, but also + * independently. There may also be scenarios where + * wl_pointer.motion is sent, but there is no relative motion. The + * order of an absolute and relative motion event originating from + * the same physical motion is not guaranteed. + * + * If the client needs button events or focus state, it can receive + * them from a wl_pointer object of the same seat that the + * wp_relative_pointer object is associated with. + * @param utime_hi high 32 bits of a 64 bit timestamp with microsecond granularity + * @param utime_lo low 32 bits of a 64 bit timestamp with microsecond granularity + * @param dx the x component of the motion vector + * @param dy the y component of the motion vector + * @param dx_unaccel the x component of the unaccelerated motion vector + * @param dy_unaccel the y component of the unaccelerated motion vector + */ + void (*relative_motion)(void *data, + struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + uint32_t utime_hi, + uint32_t utime_lo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dx_unaccel, + wl_fixed_t dy_unaccel); +}; + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +static inline int +zwp_relative_pointer_v1_add_listener(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + const struct zwp_relative_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_relative_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_RELATIVE_POINTER_V1_DESTROY 0 + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +#define ZWP_RELATIVE_POINTER_V1_RELATIVE_MOTION_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +#define ZWP_RELATIVE_POINTER_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwp_relative_pointer_v1 */ +static inline void +zwp_relative_pointer_v1_set_user_data(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_relative_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_relative_pointer_v1 */ +static inline void * +zwp_relative_pointer_v1_get_user_data(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_relative_pointer_v1); +} + +static inline uint32_t +zwp_relative_pointer_v1_get_version(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_v1); +} + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +static inline void +zwp_relative_pointer_v1_destroy(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_relative_pointer_v1, + ZWP_RELATIVE_POINTER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol-code.h new file mode 100755 index 000000000..225938653 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol-code.h @@ -0,0 +1,74 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2013-2016 Collabora, Ltd. + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface wp_viewport_interface; + +static const struct wl_interface *viewporter_types[] = { + NULL, + NULL, + NULL, + NULL, + &wp_viewport_interface, + &wl_surface_interface, +}; + +static const struct wl_message wp_viewporter_requests[] = { + { "destroy", "", viewporter_types + 0 }, + { "get_viewport", "no", viewporter_types + 4 }, +}; + +WL_PRIVATE const struct wl_interface wp_viewporter_interface = { + "wp_viewporter", 1, + 2, wp_viewporter_requests, + 0, NULL, +}; + +static const struct wl_message wp_viewport_requests[] = { + { "destroy", "", viewporter_types + 0 }, + { "set_source", "ffff", viewporter_types + 0 }, + { "set_destination", "ii", viewporter_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wp_viewport_interface = { + "wp_viewport", 1, + 3, wp_viewport_requests, + 0, NULL, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol.h b/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol.h new file mode 100755 index 000000000..b09cb200f --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-viewporter-client-protocol.h @@ -0,0 +1,398 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef VIEWPORTER_CLIENT_PROTOCOL_H +#define VIEWPORTER_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_viewporter The viewporter protocol + * @section page_ifaces_viewporter Interfaces + * - @subpage page_iface_wp_viewporter - surface cropping and scaling + * - @subpage page_iface_wp_viewport - crop and scale interface to a wl_surface + * @section page_copyright_viewporter Copyright + *
+ *
+ * Copyright © 2013-2016 Collabora, Ltd.
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct wl_surface; +struct wp_viewport; +struct wp_viewporter; + +#ifndef WP_VIEWPORTER_INTERFACE +#define WP_VIEWPORTER_INTERFACE +/** + * @page page_iface_wp_viewporter wp_viewporter + * @section page_iface_wp_viewporter_desc Description + * + * The global interface exposing surface cropping and scaling + * capabilities is used to instantiate an interface extension for a + * wl_surface object. This extended interface will then allow + * cropping and scaling the surface contents, effectively + * disconnecting the direct relationship between the buffer and the + * surface size. + * @section page_iface_wp_viewporter_api API + * See @ref iface_wp_viewporter. + */ +/** + * @defgroup iface_wp_viewporter The wp_viewporter interface + * + * The global interface exposing surface cropping and scaling + * capabilities is used to instantiate an interface extension for a + * wl_surface object. This extended interface will then allow + * cropping and scaling the surface contents, effectively + * disconnecting the direct relationship between the buffer and the + * surface size. + */ +extern const struct wl_interface wp_viewporter_interface; +#endif +#ifndef WP_VIEWPORT_INTERFACE +#define WP_VIEWPORT_INTERFACE +/** + * @page page_iface_wp_viewport wp_viewport + * @section page_iface_wp_viewport_desc Description + * + * An additional interface to a wl_surface object, which allows the + * client to specify the cropping and scaling of the surface + * contents. + * + * This interface works with two concepts: the source rectangle (src_x, + * src_y, src_width, src_height), and the destination size (dst_width, + * dst_height). The contents of the source rectangle are scaled to the + * destination size, and content outside the source rectangle is ignored. + * This state is double-buffered, and is applied on the next + * wl_surface.commit. + * + * The two parts of crop and scale state are independent: the source + * rectangle, and the destination size. Initially both are unset, that + * is, no scaling is applied. The whole of the current wl_buffer is + * used as the source, and the surface size is as defined in + * wl_surface.attach. + * + * If the destination size is set, it causes the surface size to become + * dst_width, dst_height. The source (rectangle) is scaled to exactly + * this size. This overrides whatever the attached wl_buffer size is, + * unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface + * has no content and therefore no size. Otherwise, the size is always + * at least 1x1 in surface local coordinates. + * + * If the source rectangle is set, it defines what area of the wl_buffer is + * taken as the source. If the source rectangle is set and the destination + * size is not set, then src_width and src_height must be integers, and the + * surface size becomes the source rectangle size. This results in cropping + * without scaling. If src_width or src_height are not integers and + * destination size is not set, the bad_size protocol error is raised when + * the surface state is applied. + * + * The coordinate transformations from buffer pixel coordinates up to + * the surface-local coordinates happen in the following order: + * 1. buffer_transform (wl_surface.set_buffer_transform) + * 2. buffer_scale (wl_surface.set_buffer_scale) + * 3. crop and scale (wp_viewport.set*) + * This means, that the source rectangle coordinates of crop and scale + * are given in the coordinates after the buffer transform and scale, + * i.e. in the coordinates that would be the surface-local coordinates + * if the crop and scale was not applied. + * + * If src_x or src_y are negative, the bad_value protocol error is raised. + * Otherwise, if the source rectangle is partially or completely outside of + * the non-NULL wl_buffer, then the out_of_buffer protocol error is raised + * when the surface state is applied. A NULL wl_buffer does not raise the + * out_of_buffer error. + * + * If the wl_surface associated with the wp_viewport is destroyed, + * all wp_viewport requests except 'destroy' raise the protocol error + * no_surface. + * + * If the wp_viewport object is destroyed, the crop and scale + * state is removed from the wl_surface. The change will be applied + * on the next wl_surface.commit. + * @section page_iface_wp_viewport_api API + * See @ref iface_wp_viewport. + */ +/** + * @defgroup iface_wp_viewport The wp_viewport interface + * + * An additional interface to a wl_surface object, which allows the + * client to specify the cropping and scaling of the surface + * contents. + * + * This interface works with two concepts: the source rectangle (src_x, + * src_y, src_width, src_height), and the destination size (dst_width, + * dst_height). The contents of the source rectangle are scaled to the + * destination size, and content outside the source rectangle is ignored. + * This state is double-buffered, and is applied on the next + * wl_surface.commit. + * + * The two parts of crop and scale state are independent: the source + * rectangle, and the destination size. Initially both are unset, that + * is, no scaling is applied. The whole of the current wl_buffer is + * used as the source, and the surface size is as defined in + * wl_surface.attach. + * + * If the destination size is set, it causes the surface size to become + * dst_width, dst_height. The source (rectangle) is scaled to exactly + * this size. This overrides whatever the attached wl_buffer size is, + * unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface + * has no content and therefore no size. Otherwise, the size is always + * at least 1x1 in surface local coordinates. + * + * If the source rectangle is set, it defines what area of the wl_buffer is + * taken as the source. If the source rectangle is set and the destination + * size is not set, then src_width and src_height must be integers, and the + * surface size becomes the source rectangle size. This results in cropping + * without scaling. If src_width or src_height are not integers and + * destination size is not set, the bad_size protocol error is raised when + * the surface state is applied. + * + * The coordinate transformations from buffer pixel coordinates up to + * the surface-local coordinates happen in the following order: + * 1. buffer_transform (wl_surface.set_buffer_transform) + * 2. buffer_scale (wl_surface.set_buffer_scale) + * 3. crop and scale (wp_viewport.set*) + * This means, that the source rectangle coordinates of crop and scale + * are given in the coordinates after the buffer transform and scale, + * i.e. in the coordinates that would be the surface-local coordinates + * if the crop and scale was not applied. + * + * If src_x or src_y are negative, the bad_value protocol error is raised. + * Otherwise, if the source rectangle is partially or completely outside of + * the non-NULL wl_buffer, then the out_of_buffer protocol error is raised + * when the surface state is applied. A NULL wl_buffer does not raise the + * out_of_buffer error. + * + * If the wl_surface associated with the wp_viewport is destroyed, + * all wp_viewport requests except 'destroy' raise the protocol error + * no_surface. + * + * If the wp_viewport object is destroyed, the crop and scale + * state is removed from the wl_surface. The change will be applied + * on the next wl_surface.commit. + */ +extern const struct wl_interface wp_viewport_interface; +#endif + +#ifndef WP_VIEWPORTER_ERROR_ENUM +#define WP_VIEWPORTER_ERROR_ENUM +enum wp_viewporter_error { + /** + * the surface already has a viewport object associated + */ + WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS = 0, +}; +#endif /* WP_VIEWPORTER_ERROR_ENUM */ + +#define WP_VIEWPORTER_DESTROY 0 +#define WP_VIEWPORTER_GET_VIEWPORT 1 + + +/** + * @ingroup iface_wp_viewporter + */ +#define WP_VIEWPORTER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_wp_viewporter + */ +#define WP_VIEWPORTER_GET_VIEWPORT_SINCE_VERSION 1 + +/** @ingroup iface_wp_viewporter */ +static inline void +wp_viewporter_set_user_data(struct wp_viewporter *wp_viewporter, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_viewporter, user_data); +} + +/** @ingroup iface_wp_viewporter */ +static inline void * +wp_viewporter_get_user_data(struct wp_viewporter *wp_viewporter) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_viewporter); +} + +static inline uint32_t +wp_viewporter_get_version(struct wp_viewporter *wp_viewporter) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_viewporter); +} + +/** + * @ingroup iface_wp_viewporter + * + * Informs the server that the client will not be using this + * protocol object anymore. This does not affect any other objects, + * wp_viewport objects included. + */ +static inline void +wp_viewporter_destroy(struct wp_viewporter *wp_viewporter) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_viewporter, + WP_VIEWPORTER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_viewporter), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_wp_viewporter + * + * Instantiate an interface extension for the given wl_surface to + * crop and scale its content. If the given wl_surface already has + * a wp_viewport object associated, the viewport_exists + * protocol error is raised. + */ +static inline struct wp_viewport * +wp_viewporter_get_viewport(struct wp_viewporter *wp_viewporter, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) wp_viewporter, + WP_VIEWPORTER_GET_VIEWPORT, &wp_viewport_interface, wl_proxy_get_version((struct wl_proxy *) wp_viewporter), 0, NULL, surface); + + return (struct wp_viewport *) id; +} + +#ifndef WP_VIEWPORT_ERROR_ENUM +#define WP_VIEWPORT_ERROR_ENUM +enum wp_viewport_error { + /** + * negative or zero values in width or height + */ + WP_VIEWPORT_ERROR_BAD_VALUE = 0, + /** + * destination size is not integer + */ + WP_VIEWPORT_ERROR_BAD_SIZE = 1, + /** + * source rectangle extends outside of the content area + */ + WP_VIEWPORT_ERROR_OUT_OF_BUFFER = 2, + /** + * the wl_surface was destroyed + */ + WP_VIEWPORT_ERROR_NO_SURFACE = 3, +}; +#endif /* WP_VIEWPORT_ERROR_ENUM */ + +#define WP_VIEWPORT_DESTROY 0 +#define WP_VIEWPORT_SET_SOURCE 1 +#define WP_VIEWPORT_SET_DESTINATION 2 + + +/** + * @ingroup iface_wp_viewport + */ +#define WP_VIEWPORT_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_wp_viewport + */ +#define WP_VIEWPORT_SET_SOURCE_SINCE_VERSION 1 +/** + * @ingroup iface_wp_viewport + */ +#define WP_VIEWPORT_SET_DESTINATION_SINCE_VERSION 1 + +/** @ingroup iface_wp_viewport */ +static inline void +wp_viewport_set_user_data(struct wp_viewport *wp_viewport, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_viewport, user_data); +} + +/** @ingroup iface_wp_viewport */ +static inline void * +wp_viewport_get_user_data(struct wp_viewport *wp_viewport) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_viewport); +} + +static inline uint32_t +wp_viewport_get_version(struct wp_viewport *wp_viewport) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_viewport); +} + +/** + * @ingroup iface_wp_viewport + * + * The associated wl_surface's crop and scale state is removed. + * The change is applied on the next wl_surface.commit. + */ +static inline void +wp_viewport_destroy(struct wp_viewport *wp_viewport) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_viewport, + WP_VIEWPORT_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_viewport), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_wp_viewport + * + * Set the source rectangle of the associated wl_surface. See + * wp_viewport for the description, and relation to the wl_buffer + * size. + * + * If all of x, y, width and height are -1.0, the source rectangle is + * unset instead. Any other set of values where width or height are zero + * or negative, or x or y are negative, raise the bad_value protocol + * error. + * + * The crop and scale state is double-buffered state, and will be + * applied on the next wl_surface.commit. + */ +static inline void +wp_viewport_set_source(struct wp_viewport *wp_viewport, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_viewport, + WP_VIEWPORT_SET_SOURCE, NULL, wl_proxy_get_version((struct wl_proxy *) wp_viewport), 0, x, y, width, height); +} + +/** + * @ingroup iface_wp_viewport + * + * Set the destination size of the associated wl_surface. See + * wp_viewport for the description, and relation to the wl_buffer + * size. + * + * If width is -1 and height is -1, the destination size is unset + * instead. Any other pair of values for width and height that + * contains zero or negative values raises the bad_value protocol + * error. + * + * The crop and scale state is double-buffered state, and will be + * applied on the next wl_surface.commit. + */ +static inline void +wp_viewport_set_destination(struct wp_viewport *wp_viewport, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_viewport, + WP_VIEWPORT_SET_DESTINATION, NULL, wl_proxy_get_version((struct wl_proxy *) wp_viewport), 0, width, height); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol-code.h new file mode 100755 index 000000000..9c2d471c8 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol-code.h @@ -0,0 +1,75 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2018 Simon Ser + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface xdg_toplevel_interface; +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; + +static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { + NULL, + &zxdg_toplevel_decoration_v1_interface, + &xdg_toplevel_interface, +}; + +static const struct wl_message zxdg_decoration_manager_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { + "zxdg_decoration_manager_v1", 1, + 2, zxdg_decoration_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, + { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { + { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = { + "zxdg_toplevel_decoration_v1", 1, + 3, zxdg_toplevel_decoration_v1_requests, + 1, zxdg_toplevel_decoration_v1_events, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol.h b/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol.h new file mode 100755 index 000000000..be8879fe0 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-xdg-decoration-client-protocol.h @@ -0,0 +1,378 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_decoration_unstable_v1 The xdg_decoration_unstable_v1 protocol + * @section page_ifaces_xdg_decoration_unstable_v1 Interfaces + * - @subpage page_iface_zxdg_decoration_manager_v1 - window decoration manager + * - @subpage page_iface_zxdg_toplevel_decoration_v1 - decoration object for a toplevel surface + * @section page_copyright_xdg_decoration_unstable_v1 Copyright + *
+ *
+ * Copyright © 2018 Simon Ser
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct xdg_toplevel; +struct zxdg_decoration_manager_v1; +struct zxdg_toplevel_decoration_v1; + +#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE +#define ZXDG_DECORATION_MANAGER_V1_INTERFACE +/** + * @page page_iface_zxdg_decoration_manager_v1 zxdg_decoration_manager_v1 + * @section page_iface_zxdg_decoration_manager_v1_desc Description + * + * This interface allows a compositor to announce support for server-side + * decorations. + * + * A window decoration is a set of window controls as deemed appropriate by + * the party managing them, such as user interface components used to move, + * resize and change a window's state. + * + * A client can use this protocol to request being decorated by a supporting + * compositor. + * + * If compositor and client do not negotiate the use of a server-side + * decoration using this protocol, clients continue to self-decorate as they + * see fit. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * @section page_iface_zxdg_decoration_manager_v1_api API + * See @ref iface_zxdg_decoration_manager_v1. + */ +/** + * @defgroup iface_zxdg_decoration_manager_v1 The zxdg_decoration_manager_v1 interface + * + * This interface allows a compositor to announce support for server-side + * decorations. + * + * A window decoration is a set of window controls as deemed appropriate by + * the party managing them, such as user interface components used to move, + * resize and change a window's state. + * + * A client can use this protocol to request being decorated by a supporting + * compositor. + * + * If compositor and client do not negotiate the use of a server-side + * decoration using this protocol, clients continue to self-decorate as they + * see fit. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + */ +extern const struct wl_interface zxdg_decoration_manager_v1_interface; +#endif +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +/** + * @page page_iface_zxdg_toplevel_decoration_v1 zxdg_toplevel_decoration_v1 + * @section page_iface_zxdg_toplevel_decoration_v1_desc Description + * + * The decoration object allows the compositor to toggle server-side window + * decorations for a toplevel surface. The client can request to switch to + * another mode. + * + * The xdg_toplevel_decoration object must be destroyed before its + * xdg_toplevel. + * @section page_iface_zxdg_toplevel_decoration_v1_api API + * See @ref iface_zxdg_toplevel_decoration_v1. + */ +/** + * @defgroup iface_zxdg_toplevel_decoration_v1 The zxdg_toplevel_decoration_v1 interface + * + * The decoration object allows the compositor to toggle server-side window + * decorations for a toplevel surface. The client can request to switch to + * another mode. + * + * The xdg_toplevel_decoration object must be destroyed before its + * xdg_toplevel. + */ +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; +#endif + +#define ZXDG_DECORATION_MANAGER_V1_DESTROY 0 +#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1 + + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + */ +#define ZXDG_DECORATION_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_decoration_manager_v1 + */ +#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_decoration_manager_v1 */ +static inline void +zxdg_decoration_manager_v1_set_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_decoration_manager_v1, user_data); +} + +/** @ingroup iface_zxdg_decoration_manager_v1 */ +static inline void * +zxdg_decoration_manager_v1_get_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +static inline uint32_t +zxdg_decoration_manager_v1_get_version(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + * + * Destroy the decoration manager. This doesn't destroy objects created + * with the manager. + */ +static inline void +zxdg_decoration_manager_v1_destroy(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + * + * Create a new decoration object associated with the given toplevel. + * + * Creating an xdg_toplevel_decoration from an xdg_toplevel which has a + * buffer attached or committed is a client error, and any attempts by a + * client to attach or manipulate a buffer prior to the first + * xdg_toplevel_decoration.configure event must also be treated as + * errors. + */ +static inline struct zxdg_toplevel_decoration_v1 * +zxdg_decoration_manager_v1_get_toplevel_decoration(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, struct xdg_toplevel *toplevel) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, &zxdg_toplevel_decoration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), 0, NULL, toplevel); + + return (struct zxdg_toplevel_decoration_v1 *) id; +} + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +enum zxdg_toplevel_decoration_v1_error { + /** + * xdg_toplevel has a buffer attached before configure + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER = 0, + /** + * xdg_toplevel already has a decoration object + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED = 1, + /** + * xdg_toplevel destroyed before the decoration object + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED = 2, +}; +#endif /* ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM */ + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * window decoration modes + * + * These values describe window decoration modes. + */ +enum zxdg_toplevel_decoration_v1_mode { + /** + * no server-side window decoration + */ + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE = 1, + /** + * server-side window decoration + */ + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE = 2, +}; +#endif /* ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM */ + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * @struct zxdg_toplevel_decoration_v1_listener + */ +struct zxdg_toplevel_decoration_v1_listener { + /** + * suggest a surface change + * + * The configure event asks the client to change its decoration + * mode. The configured state should not be applied immediately. + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + * + * A configure event can be sent at any time. The specified mode + * must be obeyed by the client. + * @param mode the decoration mode + */ + void (*configure)(void *data, + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + uint32_t mode); +}; + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +static inline int +zxdg_toplevel_decoration_v1_add_listener(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + const struct zxdg_toplevel_decoration_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_decoration_v1, + (void (**)(void)) listener, data); +} + +#define ZXDG_TOPLEVEL_DECORATION_V1_DESTROY 0 +#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE 1 +#define ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE 2 + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_toplevel_decoration_v1 */ +static inline void +zxdg_toplevel_decoration_v1_set_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1, user_data); +} + +/** @ingroup iface_zxdg_toplevel_decoration_v1 */ +static inline void * +zxdg_toplevel_decoration_v1_get_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +static inline uint32_t +zxdg_toplevel_decoration_v1_get_version(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Switch back to a mode without any server-side decorations at the next + * commit. + */ +static inline void +zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Set the toplevel surface decoration mode. This informs the compositor + * that the client prefers the provided decoration mode. + * + * After requesting a decoration mode, the compositor will respond by + * emitting an xdg_surface.configure event. The client should then update + * its content, drawing it without decorations if the received mode is + * server-side decorations. The client must also acknowledge the configure + * when committing the new content (see xdg_surface.ack_configure). + * + * The compositor can decide not to use the client's mode and enforce a + * different mode instead. + * + * Clients whose decoration mode depend on the xdg_toplevel state may send + * a set_mode request in response to an xdg_surface.configure event and wait + * for the next xdg_surface.configure event to prevent unwanted state. + * Such clients are responsible for preventing configure loops and must + * make sure not to send multiple successive set_mode requests with the + * same decoration mode. + */ +static inline void +zxdg_toplevel_decoration_v1_set_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0, mode); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Unset the toplevel surface decoration mode. This informs the compositor + * that the client doesn't prefer a particular decoration mode. + * + * This request has the same semantics as set_mode. + */ +static inline void +zxdg_toplevel_decoration_v1_unset_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol-code.h b/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol-code.h new file mode 100755 index 000000000..da289269d --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol-code.h @@ -0,0 +1,183 @@ +/* Generated by wayland-scanner 1.20.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 6, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 6, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 6, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 6, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 6, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol.h b/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol.h new file mode 100755 index 000000000..575159628 --- /dev/null +++ b/pkg/glfw/wayland-headers/wayland-xdg-shell-client-protocol.h @@ -0,0 +1,2307 @@ +/* Generated by wayland-scanner 1.20.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** @ingroup iface_xdg_wm_base */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is illegal + * and will result in a defunct_surfaces error. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * This creates an xdg_surface for the given surface. While xdg_surface + * itself is not a role, the corresponding surface may only be assigned + * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is + * illegal to create an xdg_surface for a wl_surface which already has an + * assigned role and this will result in a role error. + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping + * and xdg_wm_base.error.unresponsive. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines the anchor point for the anchor rectangle. The specified anchor + * is used derive an anchor point that the child surface will be + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or + * 'bottom_right'), the anchor point will be at the specified corner; + * otherwise, the derived anchor point will be centered on the specified + * edge, or in the center of the anchor rectangle if no edge is specified. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If a corner gravity is + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface + * will be placed towards the specified gravity; otherwise, the child + * surface will be centered over the anchor point on any axis that had no + * gravity specified. If the gravity is not in the ‘gravity’ enum, an + * invalid_input error is raised. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_surface */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed, otherwise + * a defunct_role_object error is raised. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_popup object for the given xdg_surface and gives + * the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be specified using + * some other protocol, before committing the initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. + * + * The window geometry is double buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * When maintaining a position, the compositor should treat the (x, y) + * coordinate of the window geometry as the top left corner of the window. + * A client changing the (x, y) window geometry coordinate should in + * general not alter the position of the window. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend outside + * of the wl_surface itself to mark parts of the subsurface tree as part of + * the window geometry. + * + * When applied, the effective window geometry will be the set window + * geometry clamped to the bounding rectangle of the combined + * geometry of the surface of the xdg_surface and the associated + * subsurfaces. + * + * The effective geometry will not be recalculated unless a new call to + * set_window_geometry is done and the new pending surface state is + * subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an invalid_serial + * error. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + * + * Sending an ack_configure request consumes the serial number sent with + * the request, as well as serial numbers sent by all configure events + * sent on this xdg_surface prior to the configure event referenced by + * the committed serial. + * + * It is an error to issue multiple ack_configure requests referencing a + * serial from the same configure event, or to issue an ack_configure + * request referencing a serial from a configure event issued before the + * event identified by the last ack_configure request for the same + * xdg_surface. Doing so will raise an invalid_serial error. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered. They will get applied on + * the next commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_xdg_toplevel */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set the "parent" of this surface. This surface should be stacked + * above the parent surface and all other ancestor surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + * + * Setting a null parent for a child surface unsets its parent. Setting + * a null parent for a surface which currently has no parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent which + * is not mapped is equivalent to setting a null parent. If a surface + * becomes unmapped, its children's parent is set to the parent of + * the now-unmapped surface. If the now-unmapped surface has no parent, + * its children's parent is unset. If the now-unmapped surface becomes + * mapped again, its parent-child relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child toplevel, + * otherwise the invalid_parent protocol error is raised. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after the + * xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains, or even if a window menu will be drawn + * at all. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, and + * is one of the values of the resize_edge enum. Values not matching + * a variant of the enum will cause the invalid_resize_edge protocol error. + * The compositor may use this information to update the surface position + * for example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an appropriate + * cursor image. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width or height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event. Whether this configure + * actually sets the window maximized is subject to compositor policies. + * The client must then update its content, drawing in the configured + * state. The client must also acknowledge the configure when committing + * the new content (see ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event. Whether this actually + * un-maximizes the window is subject to compositor policies. + * If available and applicable, the compositor will include the window + * geometry dimensions the window had prior to being maximized in the + * configure event. The client must then update its content, drawing it in + * the configured state. The client must also acknowledge the configure + * when committing the new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether the + * client is actually put into a fullscreen state is subject to compositor + * policies. The client must also acknowledge the configure when + * committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's preference as + * to which display it should be set fullscreen on. If this value is NULL, + * it's up to the compositor to choose which display will be used to map + * this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * with border fill covering the rest of the output. The content of the + * border fill is undefined, but should be assumed to be in some way that + * attempts to blend into the surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. + * Whether this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based on the following: + * * the state(s) it may have had before becoming fullscreen + * * any state(s) decided by the compositor + * * any state(s) requested by the client while the surface was fullscreen + * + * The compositor may include the previous window geometry dimensions in + * the configure event, if applicable. + * + * The client must also acknowledge the configure when committing the new + * content (see ack_configure). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** @ingroup iface_xdg_popup */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/build/Config.zig b/src/build/Config.zig index 562e1777c..e0c7ed2be 100644 --- a/src/build/Config.zig +++ b/src/build/Config.zig @@ -337,8 +337,8 @@ pub fn init(b: *std.Build) !Config { target.result.os.tag == .macos and config.app_runtime == .none and (!config.emit_bench and - !config.emit_test_exe and - !config.emit_helpgen); + !config.emit_test_exe and + !config.emit_helpgen); //--------------------------------------------------------------- // System Packages @@ -379,6 +379,11 @@ pub fn init(b: *std.Build) !Config { "glslang", "spirv-cross", "simdutf", + + // This is default false because it is used for testing + // primarily and not official packaging. The packaging + // guide advises against building the GLFW backend. + "glfw3", }) |dep| { _ = b.systemIntegrationOption(dep, .{ .default = false }); } diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index ae9f09afe..4e39c0b39 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -438,12 +438,15 @@ pub fn add( switch (self.config.app_runtime) { .none => {}, - .glfw => glfw: { - const mach_glfw_dep = b.lazyDependency("mach_glfw", .{ + .glfw => { + const glfw_dep = b.dependency("glfw", .{ .target = target, .optimize = optimize, - }) orelse break :glfw; - step.root_module.addImport("glfw", mach_glfw_dep.module("mach-glfw")); + }); + step.root_module.addImport( + "glfw", + glfw_dep.module("glfw"), + ); }, .gtk => {