├── .github ├── actions │ ├── build_test_commit │ │ └── action.yaml │ └── install_deps │ │ └── action.yaml ├── dependabot.yaml └── workflows │ ├── build.yaml │ ├── publish.yaml │ └── update.yaml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── Gtk4LayerShell-1.0.gir ├── Gtk4SessionLock-1.0.gir ├── LICENSE ├── README.md ├── generate_bindings.md ├── gtk4-layer-shell-sys ├── Cargo.toml ├── Gir.toml ├── README.md ├── build.rs ├── src │ ├── auto │ │ └── versions.txt │ ├── lib.rs │ └── manual.rs └── tests │ ├── abi.rs │ ├── constant.c │ ├── layout.c │ └── manual.h ├── gtk4-layer-shell ├── Cargo.toml ├── Gir.toml ├── README.md ├── examples │ ├── libadwaita.rs │ ├── relm4.rs │ └── simple-example.rs └── src │ ├── auto │ ├── enums.rs │ ├── functions.rs │ ├── mod.rs │ └── versions.txt │ ├── lib.rs │ └── manual.rs ├── gtk4-session-lock-sys ├── Cargo.toml ├── Gir.toml ├── README.md ├── build.rs ├── src │ ├── auto │ │ └── versions.txt │ └── lib.rs └── tests │ ├── abi.rs │ ├── constant.c │ ├── layout.c │ └── manual.h └── gtk4-session-lock ├── Cargo.toml ├── Gir.toml ├── README.md ├── examples └── simple.rs └── src ├── auto ├── instance.rs ├── mod.rs └── versions.txt ├── lib.rs └── manual.rs /.github/actions/build_test_commit/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Build, test, commit" 2 | description: "Builds the code, runs the tests and commits the changes" 3 | inputs: 4 | commit_message: 5 | description: "The commit message that will be used" 6 | required: false 7 | type: string 8 | 9 | runs: 10 | using: "composite" 11 | steps: 12 | - name: Build and test the sys crate 13 | shell: bash 14 | run: | 15 | export PATH=$PATH:/github/home/.cargo/bin 16 | 17 | folders=("gtk4-layer-shell" "gtk4-session-lock") 18 | for folder in "${folders[@]}"; do 19 | echo "Processing sys crate: ${folder}-sys" 20 | cd "${folder}-sys" || exit 1 21 | ls 22 | # Test if there is any output to the gir command 23 | if [[ $(gir -o . |& wc -l) -ne 0 ]]; then 24 | echo "gir -o failed" 25 | exit 1 26 | fi 27 | cargo update 28 | cargo build --verbose --all-features 29 | cargo test --verbose 30 | cargo doc --all-features 31 | cd .. 32 | done 33 | # Exit with an error if there is any output of the gir commands, because then there might be some wrongly generated code or errors 34 | - name: Build and test the wrapper crate 35 | shell: bash 36 | run: | 37 | export PATH=$PATH:/github/home/.cargo/bin 38 | folders=("gtk4-layer-shell" "gtk4-session-lock") 39 | for folder in "${folders[@]}"; do 40 | echo "Processing wrapper crate: ${folder}" 41 | cd "${folder}" || exit 1 42 | ls 43 | # Test gir commands 44 | if [[ $(gir -o . |& wc -l) -ne 0 ]]; then 45 | echo "gir -o failed" 46 | exit 1 47 | fi 48 | if [[ $(gir -o . -m not_bound |& wc -l) -ne 0 ]]; then 49 | echo "gir not_bound failed" 50 | exit 1 51 | fi 52 | # Check if the docs were generated without a warning 53 | if [[ $(gir -c Gir.toml --doc-target-path docs.md -m doc |& wc -l) -ne 0 ]]; then 54 | echo "gir docs failed" 55 | exit 1 56 | fi 57 | # Documentation processing 58 | cargo install rustdoc-stripper --force 59 | rustdoc-stripper -s -n --ignore src/manual.rs 60 | rustdoc-stripper -g -o docs.md 61 | # Build and test 62 | cargo update 63 | cargo build --verbose --all-features 64 | cargo test --verbose 65 | cargo doc --all-features 66 | # Build examples 67 | cargo build --examples --all-features 68 | cd .. 69 | done 70 | - name: Count the number of files other than the versions.txt files, which were changed 71 | id: changed_files 72 | shell: bash 73 | if: ${{ inputs.commit_message != '' }} 74 | run: | 75 | echo "NO_CHANGED_FILES=$(git diff --ignore-submodules --name-only -- . ':(exclude)*/src/auto/versions.txt' ':(exclude)gir' | wc -l)" >> $GITHUB_OUTPUT 76 | - name: Commit code changes to main 77 | if: inputs.commit_message != '' && steps.changed_files.outputs.NO_CHANGED_FILES != '0' 78 | uses: stefanzweifel/git-auto-commit-action@v5 79 | with: 80 | commit_message: ${{ inputs.commit_message }} -------------------------------------------------------------------------------- /.github/actions/install_deps/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Install_deps" 2 | description: "Installs the dependencies, checks out the code and sets up caching" 3 | 4 | runs: 5 | using: "composite" 6 | steps: 7 | - name: Update repos 8 | shell: bash 9 | run: sudo apt-get update 10 | 11 | - name: Hold back grub-efi-amd64-signed # TODO: Remove temporary fix 12 | shell: bash 13 | run: sudo apt-mark hold grub-efi-amd64-signed 14 | 15 | - name: Upgrade system 16 | shell: bash 17 | run: sudo apt-get upgrade -y 18 | - name: Install git and GTK3 19 | shell: bash 20 | run: sudo apt-get install -y git tar libgtk-3-dev libadwaita-1-dev # libgtk4-layer-shell-dev 21 | - name: Build gtk4-layer-shell while it is not published on ubuntu 22 | shell: bash 23 | run: | 24 | sudo apt install meson libwayland-dev libgtk-4-dev gobject-introspection libgirepository1.0-dev gtk-doc-tools valac 25 | cd .. 26 | git clone https://github.com/wmww/gtk4-layer-shell.git 27 | cd gtk4-layer-shell 28 | meson setup -Dexamples=true -Ddocs=true -Dtests=true build 29 | ninja -C build 30 | sudo ninja -C build install 31 | sudo ldconfig 32 | cd .. 33 | cd gtk4-layer-shell-gir 34 | - uses: actions/cache@v4 35 | with: 36 | path: | 37 | ~/.cargo/bin/ 38 | ~/.cargo/registry/index/ 39 | ~/.cargo/registry/cache/ 40 | ~/.cargo/git/db/ 41 | gtk4-layer-shell/target/ 42 | gtk4-layer-shell-sys/target/ 43 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 44 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | 10 | # Maintain dependencies for build_test_commit 11 | - package-ecosystem: "github-actions" 12 | directory: "/.github/actions/build_test_commit" 13 | schedule: 14 | interval: "daily" 15 | 16 | # Maintain dependencies for install_deps 17 | - package-ecosystem: "github-actions" 18 | directory: "/.github/actions/install_deps" 19 | schedule: 20 | interval: "daily" 21 | 22 | # Maintain dependencies for GitHub Workflows 23 | - package-ecosystem: "github-actions" 24 | directory: "/.github/workflows" 25 | schedule: 26 | interval: "daily" 27 | 28 | # Maintain dependencies for cargo 29 | - package-ecosystem: "cargo" 30 | directory: "/gtk4-layer-shell" 31 | schedule: 32 | interval: "daily" 33 | 34 | # Maintain dependencies for cargo 35 | - package-ecosystem: "cargo" 36 | directory: "/gtk4-layer-shell-sys" 37 | schedule: 38 | interval: "daily" 39 | 40 | # Maintain dependencies for cargo workspace 41 | - package-ecosystem: "cargo" 42 | directory: "/" 43 | schedule: 44 | interval: "daily" 45 | # Create a group of dependencies to be updated together in one pull request 46 | groups: 47 | # Name of the group 48 | gtk4: 49 | update-types: 50 | - "major" 51 | - "minor" 52 | patterns: 53 | - "glib" 54 | - "glib-sys" 55 | - "gdk4" 56 | - "gdk4-sys" 57 | - "gtk4-sys" 58 | - "gtk4" 59 | - "gio" 60 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: "Build x86" 2 | 3 | permissions: 4 | contents: read 5 | on: 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | env: 15 | CARGO_TERM_COLOR: always 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: "true" 24 | - uses: ./.github/actions/install_deps 25 | - uses: dtolnay/rust-toolchain@master 26 | with: 27 | toolchain: nightly 28 | components: rustfmt, clippy 29 | - name: Install gir 30 | working-directory: ./gir 31 | run: | 32 | ls 33 | export PATH=$PATH:/github/home/.cargo/bin 34 | cargo install --force --path . 35 | - name: Export environment variables 36 | run: | 37 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 38 | export LD_LIBRARY_PATH=/usr/local/lib 39 | - uses: ./.github/actions/build_test_commit 40 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | permissions: 4 | contents: write 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | crate_name: 9 | type: choice 10 | description: Which crate to publish? 11 | options: 12 | - gtk4-layer-shell-sys 13 | - gtk4-layer-shell 14 | - gtk4-session-lock-sys 15 | - gtk4-session-lock 16 | required: true 17 | version: 18 | description: "New version of the crate" 19 | required: true 20 | 21 | env: 22 | CARGO_TERM_COLOR: always 23 | 24 | jobs: 25 | publish: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: ./.github/actions/install_deps 30 | - uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: nightly 33 | components: rustfmt, clippy 34 | - name: Get the version number 35 | id: previous_version 36 | run: | 37 | echo "PREVIOUS_VERSION=$(grep -o -m 1 -P '(?<=version = ").*(?=")' ./${{ github.event.inputs.crate_name }}/Cargo.toml)" >> $GITHUB_OUTPUT 38 | - name: Output the version numbers 39 | run: | 40 | echo "Previous version: ${{ steps.previous_version.outputs.PREVIOUS_VERSION }}" 41 | echo "New version: ${{ github.event.inputs.version }}" 42 | - name: Bump version number in Cargo.toml 43 | run: sed -i '0,/version = "${{ steps.previous_version.outputs.PREVIOUS_VERSION }}"/{s//version = "${{ github.event.inputs.version }}"/}' ./${{ github.event.inputs.crate_name }}/Cargo.toml 44 | - name: Bump version of badge in README.md 45 | run: sed -i 's/\(${{ github.event.inputs.crate_name }}\/\)${{ steps.previous_version.outputs.PREVIOUS_VERSION }}/\1${{ github.event.inputs.version }}/g' ./${{ github.event.inputs.crate_name }}/README.md 46 | 47 | - name: Bump version of badge in repo README.md 48 | run: sed -i 's/\(${{ github.event.inputs.crate_name }}\/\)${{ steps.previous_version.outputs.PREVIOUS_VERSION }}/\1${{ github.event.inputs.version }}/g' ./README.md 49 | 50 | - name: Commit the changes to repo 51 | uses: stefanzweifel/git-auto-commit-action@v5 52 | with: 53 | commit_message: Bump version number of ${{ github.event.inputs.crate_name }} and publish it 54 | - name: Publish on crates.io 55 | env: 56 | CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }} 57 | working-directory: ./${{ github.event.inputs.crate_name }} 58 | run: cargo publish -------------------------------------------------------------------------------- /.github/workflows/update.yaml: -------------------------------------------------------------------------------- 1 | name: "Update gir and all gir-files" 2 | 3 | permissions: 4 | contents: write 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 1 * * *" 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: "true" 20 | - uses: ./.github/actions/install_deps 21 | - uses: dtolnay/rust-toolchain@master 22 | with: 23 | toolchain: nightly 24 | components: rustfmt, clippy 25 | ## Update the submodules 26 | - name: Update submodules 27 | run: | 28 | git submodule update --remote 29 | ## Update the gir submodule and install the new version 30 | - name: Install gir 31 | working-directory: ./gir 32 | run: | 33 | ls 34 | export PATH=$PATH:/github/home/.cargo/bin 35 | cargo install --force --path . 36 | - uses: ./.github/actions/build_test_commit 37 | with: 38 | commit_message: Automatically updated the version of gir or gir-files 39 | ## TODO: Enable these checks once libgtk4-layer-shell-dev is available on Ubuntu 40 | ## Check the version of the gtk4-layer-shell package 41 | ## Aborts if there was a major version bump 42 | ## - name: Get the first character of the version of the gtk4-layer-shell package to detect mayor version bumps 43 | ## id: major_version_check 44 | ## run: | 45 | ## echo "MAJOR_VERSION=$(dpkg -s libgtk4-layer-shell-dev | grep Version | sed 's/Version. 0.//' | cut -c1-1)" >> $GITHUB_OUTPUT 46 | ## echo "Version: ${{ steps.major_version_check.outputs.MAJOR_VERSION }}" 47 | ## - name: Fail if mayor version was increased 48 | ## if: steps.major_version_check.outputs.MAJOR_VERSION != '1' 49 | ## run: exit 1 50 | - uses: ./.github/actions/build_test_commit 51 | with: 52 | commit_message: Automatically updated the version of gtk-layer-shell (the C library), the gir files or gir 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | gtk4-layer-shell/docs.md 2 | Cargo.lock 3 | target -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gir"] 2 | path = gir 3 | url = https://github.com/gtk-rs/gir 4 | branch = main 5 | [submodule "gir-files"] 6 | path = gir-files 7 | url = git@github.com:gtk-rs/gir-files.git 8 | branch = main 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "gtk4-layer-shell", 4 | "gtk4-layer-shell-sys", 5 | "gtk4-session-lock", 6 | "gtk4-session-lock-sys", 7 | ] 8 | exclude = ["gir"] 9 | resolver = "2" 10 | 11 | [workspace.package] 12 | authors = ["pentamassiv "] 13 | rust-version = "1.70" 14 | edition = "2021" 15 | license = "MIT" 16 | 17 | [workspace.dependencies] 18 | libc = "0.2" 19 | bitflags = "2.0" 20 | glib-sys = "0.20" 21 | glib = "0.20" 22 | gio = "0.20" 23 | gdk4-sys = "0.9" 24 | gdk = { package = "gdk4", version = "0.9" } 25 | gtk4-sys = "0.9" 26 | gobject-sys = "0.20" 27 | gtk = { package = "gtk4", version = "0.9" } 28 | system-deps = "7" 29 | shell-words = "1.0" 30 | tempfile = "3" 31 | libadwaita = "0.7" 32 | relm4 = "0.9" 33 | gtk4-layer-shell-sys = { path = "gtk4-layer-shell-sys", version = "0.3" } 34 | gtk4-session-lock-sys = { path = "gtk4-session-lock-sys", version = "0.1" } 35 | -------------------------------------------------------------------------------- /Gtk4LayerShell-1.0.gir: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | The left edge of the screen. 23 | 24 | 25 | The right edge of the screen. 28 | 29 | 30 | The top edge of the screen. 33 | 34 | 37 | The bottom edge of the screen. 40 | 41 | 44 | Should not be used except to get the number of entries. (NOTE: may change in 47 | future releases as more entries are added) 48 | 49 | 50 | 51 | 52 | 55 | This window should not receive keyboard events. 58 | 59 | 62 | This window should have exclusive focus if it is on the top or overlay layer. 65 | 66 | 69 | The user should be able to focus and unfocues this window in an implementation 72 | defined way. Not supported for protocol version < 4. 73 | 74 | 77 | Should not be used except to get the number of entries. (NOTE: may change in 80 | future releases as more entries are added) 81 | 82 | 83 | 84 | 85 | 88 | The background layer. 91 | 92 | 95 | The bottom layer. 98 | 99 | 100 | The top layer. 103 | 104 | 107 | The overlay layer. 110 | 111 | 114 | Should not be used except to get the number of entries. (NOTE: may change in 117 | future releases as more entries are added) 118 | 119 | 120 | 122 | When auto exclusive zone is enabled, exclusive zone is automatically set to the 125 | size of the @window + relevant margin. To disable auto exclusive zone, just set the 126 | exclusive zone to 0 or any other fixed value. 127 | 128 | NOTE: you can control the auto exclusive zone by changing the margin on the non-anchored 129 | edge. This behavior is specific to gtk4-layer-shell and not part of the underlying protocol 130 | 131 | 132 | 133 | 134 | 135 | 136 | A layer surface. 139 | 140 | 141 | 142 | 143 | 145 | 146 | 147 | if the surface's exclusive zone is set to change based on the window's size 150 | 151 | 152 | 153 | 154 | A layer surface. 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | if this surface is anchored to the given edge. 167 | 168 | 169 | 170 | 171 | A layer surface. 174 | 175 | 176 | 177 | the edge to which the surface may or may not be anchored 180 | 181 | 182 | 183 | 184 | 186 | 187 | 188 | the window's exclusive zone(which may have been set manually or automatically) 191 | 192 | 193 | 194 | 195 | A layer surface. 198 | 199 | 200 | 201 | 202 | 204 | 205 | 206 | current keyboard interactivity mode for @window. 209 | 210 | 211 | 212 | 213 | A layer surface. 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | the current layer. 226 | 227 | 228 | 229 | 230 | A layer surface. 233 | 234 | 235 | 236 | 237 | 239 | 240 | 241 | the major version number of the GTK Layer Shell library 244 | 245 | 246 | 247 | 248 | 249 | 250 | the size of the margin for the given edge. 253 | 254 | 255 | 256 | 257 | A layer surface. 260 | 261 | 262 | 263 | the margin edge to get 266 | 267 | 268 | 269 | 270 | 272 | 273 | 274 | the micro/patch version number of the GTK Layer Shell library 277 | 278 | 279 | 280 | 282 | 283 | 284 | the minor version number of the GTK Layer Shell library 287 | 288 | 289 | 290 | 291 | NOTE: To get which monitor the surface is actually on, use 294 | gdk_display_get_monitor_at_window(). 295 | 296 | 297 | the monitor this surface will/has requested to be on. 300 | 301 | 302 | 303 | 304 | A layer surface. 307 | 308 | 309 | 310 | 311 | 312 | NOTE: this function does not return ownership of the string. Do not free the returned string. 315 | Future calls into the library may invalidate the returned string. 316 | 317 | 318 | a reference to the namespace property. If namespace is unset, returns the 321 | default namespace("gtk4-layer-shell"). Never returns %NULL. 322 | 323 | 324 | 325 | 326 | A layer surface. 329 | 330 | 331 | 332 | 333 | 335 | May block for a Wayland roundtrip the first time it's called. 338 | 339 | 340 | version of the zwlr_layer_shell_v1 protocol supported by the 343 | compositor or 0 if the protocol is not supported. 344 | 345 | 346 | 347 | 349 | 350 | 351 | The underlying layer surface Wayland object 354 | 355 | 356 | 357 | 358 | A layer surface. 361 | 362 | 363 | 364 | 365 | 366 | [Layer Shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1) 369 | is a Wayland protocol for desktop shell components, 370 | such as panels, notifications and wallpapers. You can use it to anchor 371 | your windows to a corner or edge of the output, or stretch them across 372 | the entire output. This library aims to support all Layer Shell features, 373 | and supports GTK popups and popovers. 374 | 375 | This library only functions on Wayland compositors that the support Layer Shell. 376 | __It does not work on X11 or GNOME on Wayland.__ 377 | 378 | # Note On Linking 379 | If you link against libwayland you must link this library before libwayland. See 380 | [linking.md](https://github.com/wmww/gtk4-layer-shell/blob/main/linking.md) for details. 381 | 382 | # Setting Window Size 383 | If you wish to force your layer surface window to be a different size than it 384 | is by default: 385 | |[<!-- language="C" --> 386 | gtk_window_set_default_size(layer_gtk_window, width, height); 387 | ]| 388 | If width or height is 0, the default is used for that axis. If the window is 389 | anchored to opposite edges of the output(see gtk_layer_set_anchor()), the 390 | size requested here is ignored. If you later wish to use the default window size 391 | repeat the call with both width and height as 0. Setting to 1, 1 is sometimes useful 392 | to keep the window the smallest it can be while still fitting its contents. 393 | 394 | 395 | Set the @window up to be a layer surface once it is mapped. this must be called before 398 | the @window is realized. 399 | 400 | 401 | 402 | 403 | 404 | 405 | A #GtkWindow to be turned into a layer surface. 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | if @window has been initialized as a layer surface. 418 | 419 | 420 | 421 | 422 | A #GtkWindow that may or may not have a layer surface. 425 | 426 | 427 | 428 | 429 | 430 | May block for a Wayland roundtrip the first time it's called. 433 | 434 | 435 | %TRUE if the platform is Wayland and Wayland compositor supports the 438 | zwlr_layer_shell_v1 protocol. 439 | 440 | 441 | 442 | 443 | Set whether @window should be anchored to @edge. 446 | - If two perpendicular edges are anchored, the surface with be anchored to that corner 447 | - If two opposite edges are anchored, the window will be stretched across the screen in that direction 448 | 449 | Default is %FALSE for each #GtkLayerShellEdge 450 | 451 | 452 | 453 | 454 | 455 | 456 | A layer surface. 459 | 460 | 461 | 462 | A #GtkLayerShellEdge this layer surface may be anchored to. 465 | 466 | 467 | 468 | Whether or not to anchor this layer surface to @edge. 471 | 472 | 473 | 474 | 475 | 477 | Has no effect unless the surface is anchored to an edge. Requests that the compositor 480 | does not place other surfaces within the given exclusive zone of the anchored edge. 481 | For example, a panel can request to not be covered by maximized windows. See 482 | wlr-layer-shell-unstable-v1.xml for details. 483 | 484 | Default is 0 485 | 486 | 487 | 488 | 489 | 490 | 491 | A layer surface. 494 | 495 | 496 | 497 | The size of the exclusive zone. 500 | 501 | 502 | 503 | 504 | 506 | Sets if/when @window should receive keyboard events from the compositor, see 509 | GtkLayerShellKeyboardMode for details. 510 | 511 | Default is %GTK_LAYER_SHELL_KEYBOARD_MODE_NONE 512 | 513 | 514 | 515 | 516 | 517 | 518 | A layer surface. 521 | 522 | 523 | 524 | The type of keyboard interactivity requested. 527 | 528 | 529 | 530 | 531 | 532 | Set the "layer" on which the surface appears(controls if it is over top of or below other surfaces). The layer may 535 | be changed on-the-fly in the current version of the layer shell protocol, but on compositors that only support an 536 | older version the @window is remapped so the change can take effect. 537 | 538 | Default is %GTK_LAYER_SHELL_LAYER_TOP 539 | 540 | 541 | 542 | 543 | 544 | 545 | A layer surface. 548 | 549 | 550 | 551 | The layer on which this surface appears. 554 | 555 | 556 | 557 | 558 | 559 | Set the margin for a specific @edge of a @window. Effects both surface's distance from 562 | the edge and its exclusive zone size(if auto exclusive zone enabled). 563 | 564 | Default is 0 for each #GtkLayerShellEdge 565 | 566 | 567 | 568 | 569 | 570 | 571 | A layer surface. 574 | 575 | 576 | 577 | The #GtkLayerShellEdge for which to set the margin. 580 | 581 | 582 | 583 | The margin for @edge to be set. 586 | 587 | 588 | 589 | 590 | 591 | Set the output for the window to be placed on, or %NULL to let the compositor choose. 594 | If the window is currently mapped, it will get remapped so the change can take effect. 595 | 596 | Default is %NULL 597 | 598 | 599 | 600 | 601 | 602 | 603 | A layer surface. 606 | 607 | 608 | 612 | The output this layer surface will be placed on (%NULL to let the compositor decide). 615 | 616 | 617 | 618 | 619 | 620 | Set the "namespace" of the surface. 623 | 624 | No one is quite sure what this is for, but it probably should be something generic 625 | ("panel", "osk", etc). The @name_space string is copied, and caller maintains 626 | ownership of original. If the window is currently mapped, it will get remapped so 627 | the change can take effect. 628 | 629 | Default is "gtk4-layer-shell" (which will be used if set to %NULL) 630 | 631 | 632 | 633 | 634 | 635 | 636 | A layer surface. 639 | 640 | 641 | 645 | The namespace of this layer surface. 648 | 649 | 650 | 651 | 652 | 653 | 654 | -------------------------------------------------------------------------------- /Gtk4SessionLock-1.0.gir: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 11 | 12 | 17 | 24 | An instance of the object used to control locking the screen. 27 | Multiple instances can exist at once, but only one can be locked at a time. 28 | 29 | 30 | 31 | 32 | new session lock instance 35 | 36 | 37 | 38 | 40 | This must be called with a different window once for each monitor, immediately after calling 43 | gtk_session_lock_lock(). Hiding a window that is active on a monitor or not letting a window be resized by the 44 | library is not allowed (may result in a Wayland protocol error). 45 | 46 | 47 | 48 | 49 | 50 | 51 | the instance to use 54 | 55 | 56 | 57 | The GTK Window to use as a lock surface 60 | 61 | 62 | 63 | The monitor to show it on 66 | 67 | 68 | 69 | 70 | 72 | Returns if this instance currently holds a lock. 75 | 76 | 77 | 78 | 79 | 80 | 81 | the instance 84 | 85 | 86 | 87 | 88 | 89 | Lock the screen. This should be called before assigning any windows to monitors. If this function fails the ::failed 92 | signal is emitted, if it succeeds the ::locked signal is emitted. The ::failed signal may be emitted before the 93 | function returns (for example, if another #GtkSessionLockInstance holds a lock) or later (if another process holds a 94 | lock). The only case where neither signal is triggered is if the instance is already locked. 95 | 96 | 97 | false on immediate fail, true if lock acquisition was successfully started 100 | 101 | 102 | 103 | 104 | the instance to lock 107 | 108 | 109 | 110 | 111 | 112 | If the screen is locked by this instance unlocks it and fires ::unlocked. Otherwise has no effect 115 | 116 | 117 | 118 | 119 | 120 | 121 | the instance to unlock 124 | 125 | 126 | 127 | 128 | 129 | The ::failed signal is fired when the lock could not be acquired. 132 | 133 | 134 | 135 | 136 | 137 | The ::locked signal is fired when the screen is successfully locked. 140 | 141 | 142 | 143 | 144 | 145 | The ::unlocked signal is fired when the session is unlocked, which may have been caused by a call to 148 | gtk_session_lock_instance_unlock() or by the compositor. 149 | 150 | 151 | 152 | 153 | 154 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | [Session Lock](https://wayland.app/protocols/ext-session-lock-v1) 166 | is a Wayland protocol for lock screens. Use it to lock the compositor 167 | and display the lock screen. This library and the underlying Wayland 168 | protocol do not handle authentication. 169 | 170 | # Note on popups 171 | Popups (such as menus and tooltips) do not currently display while the screen is locked. Please use alternatives, 172 | such as GtkPopover (which is backed by a subsurface instead of a popup). 173 | 174 | # Note On Linking 175 | If you link against libwayland you must link this library before libwayland. See 176 | [linking.md](https://github.com/wmww/gtk4-layer-shell/blob/main/linking.md) for details. 177 | 178 | 179 | May block for a Wayland roundtrip the first time it's called. 182 | 183 | 184 | %TRUE if the platform is Wayland and Wayland compositor supports the 187 | Session Lock protocol. 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 pentamassiv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gtk4-layer-shell: 2 | [![Crate](https://img.shields.io/crates/v/gtk4-layer-shell.svg)](https://crates.io/crates/gtk4-layer-shell) 3 | [![docs.rs](https://docs.rs/gtk4-layer-shell/badge.svg)](https://docs.rs/gtk4-layer-shell) 4 | [![dependency status](https://deps.rs/crate/gtk4-layer-shell/0.5.0/status.svg)](https://deps.rs/crate/gtk4-layer-shell/0.5.0) 5 | 6 | gtk4-layer-shell-sys: 7 | [![Crate](https://img.shields.io/crates/v/gtk4-layer-shell-sys.svg)](https://crates.io/crates/gtk4-layer-shell-sys) 8 | [![docs.rs](https://docs.rs/gtk4-layer-shell-sys/badge.svg)](https://docs.rs/gtk4-layer-shell-sys) 9 | [![dependency status](https://deps.rs/crate/gtk4-layer-shell-sys/0.3.0/status.svg)](https://deps.rs/crate/gtk4-layer-shell-sys/0.3.0) 10 | 11 | gtk4-session-lock: 12 | [![Crate](https://img.shields.io/crates/v/gtk4-session-lock.svg)](https://crates.io/crates/gtk4-session-lock) 13 | [![docs.rs](https://docs.rs/gtk4-session-lock/badge.svg)](https://docs.rs/gtk4-session-lock) 14 | [![dependency status](https://deps.rs/crate/gtk4-session-lock/0.1.2/status.svg)](https://deps.rs/crate/gtk4-session-lock/0.1.2) 15 | 16 | gtk4-session-lock-sys: 17 | [![Crate](https://img.shields.io/crates/v/gtk4-session-lock-sys.svg)](https://crates.io/crates/gtk4-session-lock-sys) 18 | [![docs.rs](https://docs.rs/gtk4-session-lock-sys/badge.svg)](https://docs.rs/gtk4-session-lock-sys) 19 | [![dependency status](https://deps.rs/crate/gtk4-session-lock-sys/0.1.1/status.svg)](https://deps.rs/crate/gtk4-session-lock-sys/0.1.1) 20 | 21 | [![maintenance-status: passively-maintained (as of 2023-04-12)](https://img.shields.io/badge/maintenance--status-passively--maintained_%28as_of_2023--04--12%29-forestgreen)](https://gist.github.com/rusty-snake/574a91f1df9f97ec77ca308d6d731e29) 22 | ![dependabot status](https://img.shields.io/badge/dependabot-enabled-025e8c?logo=Dependabot) 23 | [![Build](https://img.shields.io/github/actions/workflow/status/pentamassiv/gtk4-layer-shell-gir/build.yaml?branch=main)](https://github.com/pentamassiv/gtk4-layer-shell-gir/actions/workflows/build.yaml) 24 | 25 | # Contents 26 | This repo contains the following crates: 27 | 28 | - [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell): Build desktop shell components such as panels, notifications and wallpapers with GTK4. It can be used to anchor your windows to a corner or edge of the output, or stretch them across the entire output 29 | - [gtk4-layer-shell-sys](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell-sys): Unsafe bindings used by gtk-layer-shell 30 | - [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock): Build lockscreens with GTK4 31 | - [gtk4-session-lock-sys](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock-sys): Unsafe bindings used by gtk-session-lock 32 | 33 | 34 | The crates are language bindings to the underlying C library [gtk4-layer-shell](https://github.com/wmww/gtk4-layer-shell). Feature development is done upstream. The crates in this repo are automatically generated from their .gir files ([Gtk4LayerShell](Gtk4LayerShell-1.0.gir), [GTK4SessionLock](Gtk4SessionLock-1.0.gir)). For details on how to use the crates, have a look at the individual crate's README, which can be found in their respective folders. The crates are GTK4 only. 35 | 36 | ## Dependencies 37 | You need to have `gtk4` and `gtk4-layer-shell` (the C library) installed on your system to use the crates in this repo. `gtk4-layer-shell` version 1.1.0 or higher is needed if you want to use `gtk4-session-lock`. 38 | If you want to use [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell) and [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock) together in a project, make sure to use the same .so file of `gtk4-layer-shell` for both. 39 | 40 | If your distribution does not provide a current enough version of `gtk4-layer-shell`, you can [build it from source](https://github.com/wmww/gtk4-layer-shell#building-from-source). If you did that, you might also have to set the following two environment variables: 41 | ```bash 42 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 43 | export LD_LIBRARY_PATH=/usr/local/lib 44 | ``` 45 | 46 | ## Maintenance status 47 | These crates are just wrappers for the C library so the crates in this repo are feature complete and not actively worked on. The C library is actively developed. I try to keep up with them so the crate might get frequent updates and could break things. If you encounter any problems, feel free to open a PR. 48 | 49 | ## Contributing 50 | Pull requests are very welcome but please keep the maintenance status in mind. 51 | 52 | ## License 53 | [MIT](https://choosealicense.com/licenses/mit/) 54 | -------------------------------------------------------------------------------- /generate_bindings.md: -------------------------------------------------------------------------------- 1 | # Generating the bindings 2 | Generating the bindings yourself is not necessary. You can just use the version published on crates.io. If you want to do it anyways, here are the steps you need to follow. Generating the crates is a two step process. First the unsafe "-sys" crates need to be generated they are the FFI bindings to the C library. Afterwards the safe wrapper crates can get generated. 3 | 4 | ## Prerequisites 5 | You need to have the [dependencies](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/README.md#Dependencies) installed on your system. 6 | 7 | ## Generating the unsafe FFI bindings 8 | The unsafe FFI crates are automatically generated from their respective .gir file ([Gtk4LayerShell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/Gtk4LayerShell-1.0.gir) or [Gtk4SessionLock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/Gtk4SessionLock-1.0.gir)). If you want to learn more about gir, have a look at its [repo](https://github.com/gtk-rs/gir) or its [book](https://gtk-rs.org/gir/book/). 9 | 10 | Clone the repository AND the submodules "gir" and "gir-files". 11 | ```bash 12 | git clone --recurse-submodules -j8 https://github.com/pentamassiv/gtk4-layer-shell-gir.git 13 | cd ./gtk4-layer-shell-gir 14 | ``` 15 | Then you need to install gir. 16 | ```bash 17 | cd gir 18 | cargo install --path . 19 | cd .. 20 | ``` 21 | If you regenerate the binding, because you have a new version of the gir file, replace the old file with the new version. Now you can generate, build and test the bindings. 22 | ```bash 23 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 24 | # Change the directory to the folder of the sys crate you want to rebuild 25 | gir -o . # Regenerate the bindings 26 | cargo build # Build the created bindings 27 | cargo test # Test the created bindings 28 | cd .. 29 | ``` 30 | 31 | There should not have been any errors. 32 | 33 | ## Generating the safe wrapper crate 34 | Now that you have generated the bindings you will want to generate the safe wrapper. 35 | ```bash 36 | # Change the directory to the folder of the wrapper crate you want to rebuild 37 | gir -o . 38 | cargo build 39 | cargo test 40 | ``` 41 | There should not have been any errors. 42 | To make sure everything you need was created, run the following command. 43 | ```bash 44 | gir -o . -m not_bound 45 | ``` 46 | There should not be any output to this command. Let me know if there is so I can fix it. 47 | In order to build the documentation, you have to run 48 | ``` 49 | gir -c Gir.toml --doc-target-path docs.md -m doc 50 | cargo install rustdoc-stripper 51 | rustdoc-stripper -s -n 52 | rustdoc-stripper -g -o docs.md 53 | cargo doc 54 | ``` 55 | Congratulations, you've done it :-) 56 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk4-layer-shell-sys" 3 | version = "0.3.0" 4 | description = "Unsave gir-generated FFI bindings for gtk4-layer-shell" 5 | repository = "https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell-sys" 6 | documentation = "https://docs.rs/gtk4-layer-shell-sys/" 7 | keywords = ["gtk4", "gtk4-layer-shell", "wayland", "FFI", "unsafe"] 8 | categories = ["external-ffi-bindings", "gui"] 9 | build = "build.rs" 10 | 11 | [package.authors] 12 | workspace = true 13 | 14 | [package.rust-version] 15 | workspace = true 16 | 17 | [package.edition] 18 | workspace = true 19 | 20 | [package.license] 21 | workspace = true 22 | 23 | [package.metadata.system-deps.gtk4_layer_shell_0] 24 | name = "gtk4-layer-shell-0" 25 | version = "1" 26 | 27 | [package.metadata.docs.rs] 28 | all-features = true 29 | rustc-args = ["--cfg", "docsrs"] 30 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 31 | 32 | [lib] 33 | name = "gtk4_layer_shell_sys" 34 | 35 | [dependencies] 36 | libc = "0.2" 37 | 38 | [dependencies.glib-sys] 39 | workspace = true 40 | 41 | [dependencies.gdk4-sys] 42 | workspace = true 43 | 44 | [dependencies.gtk4-sys] 45 | workspace = true 46 | 47 | [build-dependencies] 48 | system-deps = "7" 49 | 50 | [dev-dependencies] 51 | shell-words = "1.0.0" 52 | tempfile = "3" 53 | 54 | [features] 55 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/Gir.toml: -------------------------------------------------------------------------------- 1 | [options] 2 | library = "Gtk4LayerShell" 3 | version = "1.0" 4 | target_path = "." 5 | min_cfg_version = "1.0" 6 | work_mode = "sys" 7 | girs_directories = ["../gir-files", ".."] 8 | single_version_file = true 9 | 10 | external_libraries = ["GLib"] 11 | 12 | [external_libraries] 13 | gdk4 = "Gdk" 14 | gtk4 = "Gtk" 15 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/README.md: -------------------------------------------------------------------------------- 1 | [![Crate](https://img.shields.io/crates/v/gtk4-layer-shell-sys.svg)](https://crates.io/crates/gtk4-layer-shell-sys) 2 | [![docs.rs](https://docs.rs/gtk4-layer-shell-sys/badge.svg)](https://docs.rs/gtk4-layer-shell-sys) 3 | [![dependency status](https://deps.rs/crate/gtk4-layer-shell-sys/0.3.0/status.svg)](https://deps.rs/crate/gtk4-layer-shell-sys/0.3.0) 4 | 5 | [![maintenance-status: passively-maintained (as of 2022-10-01)](https://img.shields.io/badge/maintenance--status-passively--maintained_%28as_of_2022--10--01%29-forestgreen)](https://gist.github.com/rusty-snake/574a91f1df9f97ec77ca308d6d731e29) 6 | ![dependabot status](https://img.shields.io/badge/dependabot-enabled-025e8c?logo=Dependabot) 7 | [![Build](https://img.shields.io/github/actions/workflow/status/pentamassiv/gtk4-layer-shell-gir/build.yaml?branch=main)](https://github.com/pentamassiv/gtk4-layer-shell-gir/actions/workflows/build.yaml) 8 | 9 | 10 | # gtk4-layer-shell-sys 11 | These are the unsafe FFI bindings for [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell). You likely want to use that crate instead. It allows building desktop shell components such as panels, notifications and wallpapers with GTK4. It can be used to anchor your windows to a corner or edge of the output, or stretch them across the entire output. To do that, it uses the [Layer Shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1) Wayland protocol. A list of supported compositors can be found [here](https://wayland.app/protocols/wlr-layer-shell-unstable-v1#compositor-support). 12 | 13 | ## Dependencies 14 | You need to have `gtk4` and `gtk4-layer-shell` (the C library) installed on your system. `gtk4-layer-shell` version 1.1.0 or higher is needed to use `gtk4-session-lock`. If you want to use [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell) and [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock) together in a project, make sure to use the same .so file of `gtk4-layer-shell` for both. 15 | 16 | If your distribution does not provide a current enough version of `gtk4-layer-shell`, you can [build it from source](https://github.com/wmww/gtk4-layer-shell#building-from-source). If you did that, you might also have to set the following two environment variables: 17 | ```bash 18 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 19 | export LD_LIBRARY_PATH=/usr/local/lib 20 | ``` 21 | 22 | ## Generating the bindings 23 | Generating the bindings yourself is not necessary to be able to use them. You can just use the version published on crates.io. If you want to do it anyways, you can find a description [here](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/generate_bindings.md). 24 | 25 | ## Maintenance status 26 | This crate is just an unsafe wrapper for the C library so the bindings are feature complete and not actively worked on. The C library is actively developed and I keep the bindings up-to-date with it. If you encounter any problems, feel free to open a PR. 27 | 28 | ## Contributing 29 | Pull requests are very welcome but please keep the maintenance status in mind. 30 | 31 | ## License 32 | [MIT](https://choosealicense.com/licenses/mit/) 33 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/build.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #[cfg(not(docsrs))] 7 | use std::process; 8 | 9 | #[cfg(docsrs)] 10 | fn main() {} // prevent linking libraries to avoid documentation failure 11 | 12 | #[cfg(not(docsrs))] 13 | fn main() { 14 | if let Err(s) = system_deps::Config::new().probe() { 15 | println!("cargo:warning={s}"); 16 | process::exit(1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/src/auto/versions.txt: -------------------------------------------------------------------------------- 1 | Generated by gir (https://github.com/gtk-rs/gir @ 5ce24253f28c) 2 | from .. (@ 1ecf663bd4c6+) 3 | from ../gir-files (@ 07aa7d177618) 4 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] 7 | #![allow(clippy::approx_constant, clippy::type_complexity, clippy::unreadable_literal, clippy::upper_case_acronyms)] 8 | #![cfg_attr(docsrs, feature(doc_cfg))] 9 | 10 | use glib_sys as glib; 11 | use gdk4_sys as gdk; 12 | use gtk4_sys as gtk; 13 | 14 | mod manual; 15 | 16 | pub use manual::*; 17 | 18 | #[allow(unused_imports)] 19 | use std::ffi::{c_int, c_char, c_uchar, c_float, c_uint, c_double, 20 | c_short, c_ushort, c_long, c_ulong, c_void}; 21 | #[allow(unused_imports)] 22 | use libc::{size_t, ssize_t, time_t, off_t, intptr_t, uintptr_t, FILE}; 23 | #[cfg(unix)] 24 | #[allow(unused_imports)] 25 | use libc::{dev_t, gid_t, pid_t, socklen_t, uid_t}; 26 | 27 | #[allow(unused_imports)] 28 | use glib::{gboolean, gconstpointer, gpointer, GType}; 29 | 30 | // Enums 31 | pub type GtkLayerShellEdge = c_int; 32 | pub const GTK_LAYER_SHELL_EDGE_LEFT: GtkLayerShellEdge = 0; 33 | pub const GTK_LAYER_SHELL_EDGE_RIGHT: GtkLayerShellEdge = 1; 34 | pub const GTK_LAYER_SHELL_EDGE_TOP: GtkLayerShellEdge = 2; 35 | pub const GTK_LAYER_SHELL_EDGE_BOTTOM: GtkLayerShellEdge = 3; 36 | pub const GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER: GtkLayerShellEdge = 4; 37 | 38 | pub type GtkLayerShellKeyboardMode = c_int; 39 | pub const GTK_LAYER_SHELL_KEYBOARD_MODE_NONE: GtkLayerShellKeyboardMode = 0; 40 | pub const GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE: GtkLayerShellKeyboardMode = 1; 41 | pub const GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND: GtkLayerShellKeyboardMode = 2; 42 | pub const GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER: GtkLayerShellKeyboardMode = 3; 43 | 44 | pub type GtkLayerShellLayer = c_int; 45 | pub const GTK_LAYER_SHELL_LAYER_BACKGROUND: GtkLayerShellLayer = 0; 46 | pub const GTK_LAYER_SHELL_LAYER_BOTTOM: GtkLayerShellLayer = 1; 47 | pub const GTK_LAYER_SHELL_LAYER_TOP: GtkLayerShellLayer = 2; 48 | pub const GTK_LAYER_SHELL_LAYER_OVERLAY: GtkLayerShellLayer = 3; 49 | pub const GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER: GtkLayerShellLayer = 4; 50 | 51 | extern "C" { 52 | 53 | //========================================================================= 54 | // Other functions 55 | //========================================================================= 56 | pub fn gtk_layer_auto_exclusive_zone_enable(window: *mut gtk::GtkWindow); 57 | pub fn gtk_layer_auto_exclusive_zone_is_enabled(window: *mut gtk::GtkWindow) -> gboolean; 58 | pub fn gtk_layer_get_anchor(window: *mut gtk::GtkWindow, edge: GtkLayerShellEdge) -> gboolean; 59 | pub fn gtk_layer_get_exclusive_zone(window: *mut gtk::GtkWindow) -> c_int; 60 | pub fn gtk_layer_get_keyboard_mode(window: *mut gtk::GtkWindow) -> GtkLayerShellKeyboardMode; 61 | pub fn gtk_layer_get_layer(window: *mut gtk::GtkWindow) -> GtkLayerShellLayer; 62 | pub fn gtk_layer_get_major_version() -> c_uint; 63 | pub fn gtk_layer_get_margin(window: *mut gtk::GtkWindow, edge: GtkLayerShellEdge) -> c_int; 64 | pub fn gtk_layer_get_micro_version() -> c_uint; 65 | pub fn gtk_layer_get_minor_version() -> c_uint; 66 | pub fn gtk_layer_get_monitor(window: *mut gtk::GtkWindow) -> *mut gdk::GdkMonitor; 67 | pub fn gtk_layer_get_namespace(window: *mut gtk::GtkWindow) -> *const c_char; 68 | pub fn gtk_layer_get_protocol_version() -> c_uint; 69 | pub fn gtk_layer_get_zwlr_layer_surface_v1(window: *mut gtk::GtkWindow) -> *mut zwlr_layer_surface_v1; 70 | pub fn gtk_layer_init_for_window(window: *mut gtk::GtkWindow); 71 | pub fn gtk_layer_is_layer_window(window: *mut gtk::GtkWindow) -> gboolean; 72 | pub fn gtk_layer_is_supported() -> gboolean; 73 | pub fn gtk_layer_set_anchor(window: *mut gtk::GtkWindow, edge: GtkLayerShellEdge, anchor_to_edge: gboolean); 74 | pub fn gtk_layer_set_exclusive_zone(window: *mut gtk::GtkWindow, exclusive_zone: c_int); 75 | pub fn gtk_layer_set_keyboard_mode(window: *mut gtk::GtkWindow, mode: GtkLayerShellKeyboardMode); 76 | pub fn gtk_layer_set_layer(window: *mut gtk::GtkWindow, layer: GtkLayerShellLayer); 77 | pub fn gtk_layer_set_margin(window: *mut gtk::GtkWindow, edge: GtkLayerShellEdge, margin_size: c_int); 78 | pub fn gtk_layer_set_monitor(window: *mut gtk::GtkWindow, monitor: *mut gdk::GdkMonitor); 79 | pub fn gtk_layer_set_namespace(window: *mut gtk::GtkWindow, name_space: *const c_char); 80 | 81 | } 82 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/src/manual.rs: -------------------------------------------------------------------------------- 1 | use glib_sys::gpointer; 2 | 3 | pub type zwlr_layer_surface_v1 = gpointer; 4 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/tests/abi.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #![cfg(unix)] 7 | 8 | use gtk4_layer_shell_sys::*; 9 | use std::mem::{align_of, size_of}; 10 | use std::env; 11 | use std::error::Error; 12 | use std::ffi::OsString; 13 | use std::path::Path; 14 | use std::process::{Command, Stdio}; 15 | use std::str; 16 | use tempfile::Builder; 17 | 18 | static PACKAGES: &[&str] = &["gtk4-layer-shell-0"]; 19 | 20 | #[derive(Clone, Debug)] 21 | struct Compiler { 22 | pub args: Vec, 23 | } 24 | 25 | impl Compiler { 26 | pub fn new() -> Result> { 27 | let mut args = get_var("CC", "cc")?; 28 | args.push("-Wno-deprecated-declarations".to_owned()); 29 | // For _Generic 30 | args.push("-std=c11".to_owned()); 31 | // For %z support in printf when using MinGW. 32 | args.push("-D__USE_MINGW_ANSI_STDIO".to_owned()); 33 | args.extend(get_var("CFLAGS", "")?); 34 | args.extend(get_var("CPPFLAGS", "")?); 35 | args.extend(pkg_config_cflags(PACKAGES)?); 36 | Ok(Self { args }) 37 | } 38 | 39 | pub fn compile(&self, src: &Path, out: &Path) -> Result<(), Box> { 40 | let mut cmd = self.to_command(); 41 | cmd.arg(src); 42 | cmd.arg("-o"); 43 | cmd.arg(out); 44 | let status = cmd.spawn()?.wait()?; 45 | if !status.success() { 46 | return Err(format!("compilation command {cmd:?} failed, {status}").into()); 47 | } 48 | Ok(()) 49 | } 50 | 51 | fn to_command(&self) -> Command { 52 | let mut cmd = Command::new(&self.args[0]); 53 | cmd.args(&self.args[1..]); 54 | cmd 55 | } 56 | } 57 | 58 | fn get_var(name: &str, default: &str) -> Result, Box> { 59 | match env::var(name) { 60 | Ok(value) => Ok(shell_words::split(&value)?), 61 | Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?), 62 | Err(err) => Err(format!("{name} {err}").into()), 63 | } 64 | } 65 | 66 | fn pkg_config_cflags(packages: &[&str]) -> Result, Box> { 67 | if packages.is_empty() { 68 | return Ok(Vec::new()); 69 | } 70 | let pkg_config = env::var_os("PKG_CONFIG") 71 | .unwrap_or_else(|| OsString::from("pkg-config")); 72 | let mut cmd = Command::new(pkg_config); 73 | cmd.arg("--cflags"); 74 | cmd.args(packages); 75 | cmd.stderr(Stdio::inherit()); 76 | let out = cmd.output()?; 77 | if !out.status.success() { 78 | let (status, stdout) = (out.status, String::from_utf8_lossy(&out.stdout)); 79 | return Err(format!("command {cmd:?} failed, {status:?}\nstdout: {stdout}").into()); 80 | } 81 | let stdout = str::from_utf8(&out.stdout)?; 82 | Ok(shell_words::split(stdout.trim())?) 83 | } 84 | 85 | 86 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 87 | struct Layout { 88 | size: usize, 89 | alignment: usize, 90 | } 91 | 92 | #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 93 | struct Results { 94 | /// Number of successfully completed tests. 95 | passed: usize, 96 | /// Total number of failed tests (including those that failed to compile). 97 | failed: usize, 98 | } 99 | 100 | impl Results { 101 | fn record_passed(&mut self) { 102 | self.passed += 1; 103 | } 104 | fn record_failed(&mut self) { 105 | self.failed += 1; 106 | } 107 | fn summary(&self) -> String { 108 | format!("{} passed; {} failed", self.passed, self.failed) 109 | } 110 | fn expect_total_success(&self) { 111 | if self.failed == 0 { 112 | println!("OK: {}", self.summary()); 113 | } else { 114 | panic!("FAILED: {}", self.summary()); 115 | }; 116 | } 117 | } 118 | 119 | #[test] 120 | fn cross_validate_constants_with_c() { 121 | let mut c_constants: Vec<(String, String)> = Vec::new(); 122 | 123 | for l in get_c_output("constant").unwrap().lines() { 124 | let (name, value) = l.split_once(';').expect("Missing ';' separator"); 125 | c_constants.push((name.to_owned(), value.to_owned())); 126 | } 127 | 128 | let mut results = Results::default(); 129 | 130 | for ((rust_name, rust_value), (c_name, c_value)) in 131 | RUST_CONSTANTS.iter().zip(c_constants.iter()) 132 | { 133 | if rust_name != c_name { 134 | results.record_failed(); 135 | eprintln!("Name mismatch:\nRust: {rust_name:?}\nC: {c_name:?}"); 136 | continue; 137 | } 138 | 139 | if rust_value != c_value { 140 | results.record_failed(); 141 | eprintln!( 142 | "Constant value mismatch for {rust_name}\nRust: {rust_value:?}\nC: {c_value:?}", 143 | ); 144 | continue; 145 | } 146 | 147 | results.record_passed(); 148 | } 149 | 150 | results.expect_total_success(); 151 | } 152 | 153 | #[test] 154 | fn cross_validate_layout_with_c() { 155 | let mut c_layouts = Vec::new(); 156 | 157 | for l in get_c_output("layout").unwrap().lines() { 158 | let (name, value) = l.split_once(';').expect("Missing first ';' separator"); 159 | let (size, alignment) = value.split_once(';').expect("Missing second ';' separator"); 160 | let size = size.parse().expect("Failed to parse size"); 161 | let alignment = alignment.parse().expect("Failed to parse alignment"); 162 | c_layouts.push((name.to_owned(), Layout { size, alignment })); 163 | } 164 | 165 | let mut results = Results::default(); 166 | 167 | for ((rust_name, rust_layout), (c_name, c_layout)) in 168 | RUST_LAYOUTS.iter().zip(c_layouts.iter()) 169 | { 170 | if rust_name != c_name { 171 | results.record_failed(); 172 | eprintln!("Name mismatch:\nRust: {rust_name:?}\nC: {c_name:?}"); 173 | continue; 174 | } 175 | 176 | if rust_layout != c_layout { 177 | results.record_failed(); 178 | eprintln!( 179 | "Layout mismatch for {rust_name}\nRust: {rust_layout:?}\nC: {c_layout:?}", 180 | ); 181 | continue; 182 | } 183 | 184 | results.record_passed(); 185 | } 186 | 187 | results.expect_total_success(); 188 | } 189 | 190 | fn get_c_output(name: &str) -> Result> { 191 | let tmpdir = Builder::new().prefix("abi").tempdir()?; 192 | let exe = tmpdir.path().join(name); 193 | let c_file = Path::new("tests").join(name).with_extension("c"); 194 | 195 | let cc = Compiler::new().expect("configured compiler"); 196 | cc.compile(&c_file, &exe)?; 197 | 198 | let mut cmd = Command::new(exe); 199 | cmd.stderr(Stdio::inherit()); 200 | let out = cmd.output()?; 201 | if !out.status.success() { 202 | let (status, stdout) = (out.status, String::from_utf8_lossy(&out.stdout)); 203 | return Err(format!("command {cmd:?} failed, {status:?}\nstdout: {stdout}").into()); 204 | } 205 | 206 | Ok(String::from_utf8(out.stdout)?) 207 | } 208 | 209 | const RUST_LAYOUTS: &[(&str, Layout)] = &[ 210 | ("GtkLayerShellEdge", Layout {size: size_of::(), alignment: align_of::()}), 211 | ("GtkLayerShellKeyboardMode", Layout {size: size_of::(), alignment: align_of::()}), 212 | ("GtkLayerShellLayer", Layout {size: size_of::(), alignment: align_of::()}), 213 | ]; 214 | 215 | const RUST_CONSTANTS: &[(&str, &str)] = &[ 216 | ("(gint) GTK_LAYER_SHELL_EDGE_BOTTOM", "3"), 217 | ("(gint) GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER", "4"), 218 | ("(gint) GTK_LAYER_SHELL_EDGE_LEFT", "0"), 219 | ("(gint) GTK_LAYER_SHELL_EDGE_RIGHT", "1"), 220 | ("(gint) GTK_LAYER_SHELL_EDGE_TOP", "2"), 221 | ("(gint) GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER", "3"), 222 | ("(gint) GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE", "1"), 223 | ("(gint) GTK_LAYER_SHELL_KEYBOARD_MODE_NONE", "0"), 224 | ("(gint) GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND", "2"), 225 | ("(gint) GTK_LAYER_SHELL_LAYER_BACKGROUND", "0"), 226 | ("(gint) GTK_LAYER_SHELL_LAYER_BOTTOM", "1"), 227 | ("(gint) GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER", "4"), 228 | ("(gint) GTK_LAYER_SHELL_LAYER_OVERLAY", "3"), 229 | ("(gint) GTK_LAYER_SHELL_LAYER_TOP", "2"), 230 | ]; 231 | 232 | 233 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/tests/constant.c: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #include "manual.h" 7 | #include 8 | 9 | #define PRINT_CONSTANT(CONSTANT_NAME) \ 10 | printf("%s;", #CONSTANT_NAME); \ 11 | printf(_Generic((CONSTANT_NAME), \ 12 | char *: "%s", \ 13 | const char *: "%s", \ 14 | char: "%c", \ 15 | signed char: "%hhd", \ 16 | unsigned char: "%hhu", \ 17 | short int: "%hd", \ 18 | unsigned short int: "%hu", \ 19 | int: "%d", \ 20 | unsigned int: "%u", \ 21 | long: "%ld", \ 22 | unsigned long: "%lu", \ 23 | long long: "%lld", \ 24 | unsigned long long: "%llu", \ 25 | float: "%f", \ 26 | double: "%f", \ 27 | long double: "%ld"), \ 28 | CONSTANT_NAME); \ 29 | printf("\n"); 30 | 31 | int main() { 32 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_EDGE_BOTTOM); 33 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER); 34 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_EDGE_LEFT); 35 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_EDGE_RIGHT); 36 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_EDGE_TOP); 37 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER); 38 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE); 39 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); 40 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); 41 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_LAYER_BACKGROUND); 42 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_LAYER_BOTTOM); 43 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER); 44 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_LAYER_OVERLAY); 45 | PRINT_CONSTANT((gint) GTK_LAYER_SHELL_LAYER_TOP); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/tests/layout.c: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #include "manual.h" 7 | #include 8 | #include 9 | 10 | int main() { 11 | printf("%s;%zu;%zu\n", "GtkLayerShellEdge", sizeof(GtkLayerShellEdge), alignof(GtkLayerShellEdge)); 12 | printf("%s;%zu;%zu\n", "GtkLayerShellKeyboardMode", sizeof(GtkLayerShellKeyboardMode), alignof(GtkLayerShellKeyboardMode)); 13 | printf("%s;%zu;%zu\n", "GtkLayerShellLayer", sizeof(GtkLayerShellLayer), alignof(GtkLayerShellLayer)); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /gtk4-layer-shell-sys/tests/manual.h: -------------------------------------------------------------------------------- 1 | // Feel free to edit this file, it won't be regenerated by gir generator unless removed. 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /gtk4-layer-shell/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk4-layer-shell" 3 | version = "0.5.0" 4 | description = "Save gir-generated wrapper for gtk4-layer-shell" 5 | repository = "https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell" 6 | documentation = "https://docs.rs/gtk4-layer-shell/" 7 | keywords = ["gtk4", "gtk4-layer-shell", "wayland", "gir", "wrapper"] 8 | categories = ["api-bindings", "gui"] 9 | exclude = ["examples"] 10 | authors.workspace = true 11 | rust-version.workspace = true 12 | edition.workspace = true 13 | license.workspace = true 14 | 15 | [package.metadata.docs.rs] 16 | all-features = true 17 | rustc-args = ["--cfg", "docsrs"] 18 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 19 | 20 | [features] 21 | 22 | [dependencies] 23 | libc.workspace = true 24 | bitflags.workspace = true 25 | glib.workspace = true 26 | glib-sys.workspace = true 27 | gdk.workspace = true 28 | gtk.workspace = true 29 | gtk4-layer-shell-sys.workspace = true 30 | 31 | [dev-dependencies] 32 | gio.workspace = true 33 | libadwaita.workspace = true 34 | relm4.workspace = true 35 | 36 | [target.'cfg(docsrs)'.dependencies] 37 | gdk = { package = "gdk4", version = "0.9", features = ["gl", "v4_16"] } 38 | -------------------------------------------------------------------------------- /gtk4-layer-shell/Gir.toml: -------------------------------------------------------------------------------- 1 | [options] 2 | library = "Gtk4LayerShell" 3 | version = "1.0" 4 | target_path = "." 5 | min_cfg_version = "1.0" 6 | work_mode = "normal" 7 | girs_directories = ["../gir-files", ".."] 8 | generate_safety_asserts = true 9 | deprecate_by_min_version = true 10 | single_version_file = true 11 | 12 | generate = [ 13 | "Gtk4LayerShell.Edge", 14 | "Gtk4LayerShell.Layer", 15 | "Gtk4LayerShell.KeyboardMode", 16 | "Gtk4LayerShell.*", 17 | ] 18 | 19 | manual = ["Gtk.Window", "Gdk.Monitor"] 20 | -------------------------------------------------------------------------------- /gtk4-layer-shell/README.md: -------------------------------------------------------------------------------- 1 | [![Crate](https://img.shields.io/crates/v/gtk4-layer-shell.svg)](https://crates.io/crates/gtk4-layer-shell) 2 | [![docs.rs](https://docs.rs/gtk4-layer-shell/badge.svg)](https://docs.rs/gtk4-layer-shell) 3 | [![dependency status](https://deps.rs/crate/gtk4-layer-shell/0.5.0/status.svg)](https://deps.rs/crate/gtk4-layer-shell/0.5.0) 4 | 5 | [![maintenance-status: passively-maintained (as of 2022-10-01)](https://img.shields.io/badge/maintenance--status-passively--maintained_%28as_of_2022--10--01%29-forestgreen)](https://gist.github.com/rusty-snake/574a91f1df9f97ec77ca308d6d731e29) 6 | ![dependabot status](https://img.shields.io/badge/dependabot-enabled-025e8c?logo=Dependabot) 7 | [![Build](https://img.shields.io/github/actions/workflow/status/pentamassiv/gtk4-layer-shell-gir/build.yaml?branch=main)](https://github.com/pentamassiv/gtk4-layer-shell-gir/actions/workflows/build.yaml) 8 | 9 | # gtk4-layer-shell 10 | This crate allows building desktop shell components such as panels, notifications and wallpapers with GTK4. It can be used to anchor your windows to a corner or edge of the output, or stretch them across the entire output. To do that, it uses the [Layer Shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1) Wayland protocol. A list of supported compositors can be found [here](https://wayland.app/protocols/wlr-layer-shell-unstable-v1#compositor-support). 11 | 12 | ## Dependencies 13 | You need to have `gtk4` and `gtk4-layer-shell` (the C library) installed on your system. `gtk4-layer-shell` version 1.1.0 or higher is needed to use `gtk4-session-lock`. If you want to use [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell) and [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock) together in a project, make sure to use the same .so file of `gtk4-layer-shell` for both. 14 | 15 | If your distribution does not provide a current enough version of `gtk4-layer-shell`, you can [build it from source](https://github.com/wmww/gtk4-layer-shell#building-from-source). If you did that, you might also have to set the following two environment variables: 16 | ```bash 17 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 18 | export LD_LIBRARY_PATH=/usr/local/lib 19 | ``` 20 | 21 | ## Usage 22 | Have a look at the [simple example](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell/examples/simple-example.rs) to see how the bindings can be used. It works analogous to the original. 23 | 24 | ## Generating the wrapper 25 | Generating the bindings yourself is not necessary to be able to use them. You can just use the version published on crates.io. If you want to do it anyways, you can find a description [here](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/generate_bindings.md). 26 | 27 | ## Maintenance status 28 | This crate is just a safe wrapper for the C library so the bindings are feature complete and not actively worked on. The C library is actively developed and I keep the bindings up-to-date with it. If you encounter any problems, feel free to open a PR. 29 | 30 | ## Contributing 31 | Pull requests are very welcome but please keep the maintenance status in mind. 32 | 33 | ## License 34 | [MIT](https://choosealicense.com/licenses/mit/) 35 | -------------------------------------------------------------------------------- /gtk4-layer-shell/examples/libadwaita.rs: -------------------------------------------------------------------------------- 1 | // This example is copied from https://gitlab.gnome.org/World/Rust/libadwaita-rs/-/blob/master/libadwaita/examples/hello-world.rs 2 | // The only part that is specific to the usage of gtk4-layer-shell is encased by comments 3 | 4 | use libadwaita as adw; 5 | 6 | use adw::prelude::*; 7 | use adw::{ActionRow, ApplicationWindow, HeaderBar}; 8 | use gtk::{Application, Box, ListBox, Orientation}; 9 | use gtk4_layer_shell::{Edge, Layer, LayerShell}; 10 | 11 | fn main() { 12 | let application = Application::builder() 13 | .application_id("com.example.FirstAdwaitaApp") 14 | .build(); 15 | 16 | application.connect_startup(|_| { 17 | adw::init().unwrap(); 18 | }); 19 | 20 | application.connect_activate(|app| { 21 | // ActionRows are only available in Adwaita 22 | let row = ActionRow::builder() 23 | .activatable(true) 24 | .selectable(false) 25 | .title("Click me") 26 | .build(); 27 | row.connect_activated(|_| { 28 | eprintln!("Clicked!"); 29 | }); 30 | 31 | let list = ListBox::builder() 32 | .margin_top(32) 33 | .margin_end(32) 34 | .margin_bottom(32) 35 | .margin_start(32) 36 | // the content class makes the list look nicer 37 | .css_classes(vec![String::from("content")]) 38 | .build(); 39 | list.append(&row); 40 | 41 | // Combine the content in a box 42 | let content = Box::new(Orientation::Vertical, 0); 43 | // Adwaitas' ApplicationWindow does not include a HeaderBar 44 | content.append( 45 | &HeaderBar::builder() 46 | .title_widget(&adw::WindowTitle::new("First App", "")) 47 | .build(), 48 | ); 49 | content.append(&list); 50 | 51 | let window = ApplicationWindow::builder() 52 | .application(app) 53 | .default_width(350) 54 | // add content to window 55 | .content(&content) 56 | .build(); 57 | 58 | // ################################# 59 | // Part that is specific to use gtk4-layer-shell begins 60 | 61 | // Before the window is first realized, set it up to be a layer surface 62 | window.init_layer_shell(); 63 | 64 | // Display above normal windows 65 | window.set_layer(Layer::Overlay); 66 | 67 | // Push other windows out of the way 68 | window.auto_exclusive_zone_enable(); 69 | 70 | // Anchors are if the window is pinned to each edge of the output 71 | let anchors = [ 72 | (Edge::Left, true), 73 | (Edge::Right, true), 74 | (Edge::Top, false), 75 | (Edge::Bottom, true), 76 | ]; 77 | 78 | for (anchor, state) in anchors { 79 | window.set_anchor(anchor, state); 80 | } 81 | // Part that is specific to use gtk4-layer-shell ends 82 | // ################################# 83 | 84 | window.show(); 85 | }); 86 | 87 | application.run(); 88 | } 89 | -------------------------------------------------------------------------------- /gtk4-layer-shell/examples/relm4.rs: -------------------------------------------------------------------------------- 1 | // This is based on the simple example of the relm4 repo and is meant to showcase how easily this library can be used with relm4 2 | 3 | use gtk::prelude::*; 4 | use gtk4_layer_shell::{Edge, Layer, LayerShell}; // Import the additional types 5 | use relm4::prelude::*; 6 | 7 | struct App { 8 | counter: u8, 9 | } 10 | 11 | #[derive(Debug)] 12 | enum Msg { 13 | Increment, 14 | Decrement, 15 | } 16 | 17 | #[relm4::component] 18 | impl SimpleComponent for App { 19 | type Init = u8; 20 | type Input = Msg; 21 | type Output = (); 22 | 23 | view! { 24 | gtk::Window { 25 | init_layer_shell: (), // Do gtk4_layer_shell stuff here 26 | set_layer: Layer::Overlay, 27 | auto_exclusive_zone_enable: (), 28 | set_margin: (Edge::Left, 40), 29 | set_anchor: (Edge::Right, true), 30 | set_anchor: (Edge::Top, false), 31 | set_anchor: (Edge::Bottom, true), 32 | set_title: Some("Simple app"), 33 | set_default_size: (300, 100), 34 | 35 | gtk::Box { 36 | set_orientation: gtk::Orientation::Vertical, 37 | set_spacing: 5, 38 | set_margin_all: 5, 39 | 40 | gtk::Button { 41 | set_label: "Increment", 42 | connect_clicked => Msg::Increment, 43 | }, 44 | 45 | gtk::Button { 46 | set_label: "Decrement", 47 | connect_clicked => Msg::Decrement, 48 | }, 49 | 50 | gtk::Label { 51 | #[watch] 52 | set_label: &format!("Counter: {}", model.counter), 53 | set_margin_all: 5, 54 | } 55 | } 56 | } 57 | } 58 | 59 | // Initialize the component. 60 | fn init( 61 | counter: Self::Init, 62 | root: Self::Root, 63 | sender: ComponentSender, 64 | ) -> ComponentParts { 65 | let model = App { counter }; 66 | 67 | // Insert the code generation of the view! macro here 68 | let widgets = view_output!(); 69 | 70 | ComponentParts { model, widgets } 71 | } 72 | 73 | fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { 74 | match msg { 75 | Msg::Increment => { 76 | self.counter = self.counter.wrapping_add(1); 77 | } 78 | Msg::Decrement => { 79 | self.counter = self.counter.wrapping_sub(1); 80 | } 81 | } 82 | } 83 | } 84 | 85 | fn main() { 86 | let app = RelmApp::new("relm4.example.simple"); 87 | app.run::(0); 88 | } 89 | -------------------------------------------------------------------------------- /gtk4-layer-shell/examples/simple-example.rs: -------------------------------------------------------------------------------- 1 | use gio::prelude::*; 2 | use gtk::prelude::*; 3 | use gtk4_layer_shell::{Edge, Layer, LayerShell}; 4 | 5 | // https://github.com/wmww/gtk-layer-shell/blob/master/examples/simple-example.c 6 | fn activate(application: >k::Application) { 7 | // Create a normal GTK window however you like 8 | let window = gtk::ApplicationWindow::new(application); 9 | 10 | // Before the window is first realized, set it up to be a layer surface 11 | window.init_layer_shell(); 12 | 13 | // Display above normal windows 14 | window.set_layer(Layer::Overlay); 15 | 16 | // Push other windows out of the way 17 | window.auto_exclusive_zone_enable(); 18 | 19 | // The margins are the gaps around the window's edges 20 | // Margins and anchors can be set like this... 21 | window.set_margin(Edge::Left, 40); 22 | window.set_margin(Edge::Right, 40); 23 | window.set_margin(Edge::Top, 20); 24 | 25 | // ... or like this 26 | // Anchors are if the window is pinned to each edge of the output 27 | let anchors = [ 28 | (Edge::Left, true), 29 | (Edge::Right, true), 30 | (Edge::Top, false), 31 | (Edge::Bottom, true), 32 | ]; 33 | 34 | for (anchor, state) in anchors { 35 | window.set_anchor(anchor, state); 36 | } 37 | 38 | // Set up a widget 39 | let label = gtk::Label::new(Some("")); 40 | label.set_markup("GTK Layer Shell example!"); 41 | window.set_child(Some(&label)); 42 | window.show() 43 | } 44 | 45 | fn main() { 46 | let application = gtk::Application::new(Some("sh.wmww.gtk-layer-example"), Default::default()); 47 | 48 | application.connect_activate(|app| { 49 | activate(app); 50 | }); 51 | 52 | application.run(); 53 | } 54 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/auto/enums.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | use crate::{ffi}; 7 | use glib::{translate::*}; 8 | 9 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 10 | #[derive(Clone, Copy)] 11 | #[non_exhaustive] 12 | #[doc(alias = "GtkLayerShellEdge")] 13 | pub enum Edge { 14 | /// The left edge of the screen. 15 | #[doc(alias = "GTK_LAYER_SHELL_EDGE_LEFT")] 16 | Left, 17 | /// The right edge of the screen. 18 | #[doc(alias = "GTK_LAYER_SHELL_EDGE_RIGHT")] 19 | Right, 20 | /// The top edge of the screen. 21 | #[doc(alias = "GTK_LAYER_SHELL_EDGE_TOP")] 22 | Top, 23 | /// The bottom edge of the screen. 24 | #[doc(alias = "GTK_LAYER_SHELL_EDGE_BOTTOM")] 25 | Bottom, 26 | /// Should not be used except to get the number of entries. (NOTE: may change in 27 | /// future releases as more entries are added) 28 | #[doc(alias = "GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER")] 29 | EntryNumber, 30 | #[doc(hidden)] 31 | __Unknown(i32), 32 | } 33 | 34 | #[doc(hidden)] 35 | impl IntoGlib for Edge { 36 | type GlibType = ffi::GtkLayerShellEdge; 37 | 38 | #[inline] 39 | fn into_glib(self) -> ffi::GtkLayerShellEdge { 40 | match self { 41 | Self::Left => ffi::GTK_LAYER_SHELL_EDGE_LEFT, 42 | Self::Right => ffi::GTK_LAYER_SHELL_EDGE_RIGHT, 43 | Self::Top => ffi::GTK_LAYER_SHELL_EDGE_TOP, 44 | Self::Bottom => ffi::GTK_LAYER_SHELL_EDGE_BOTTOM, 45 | Self::EntryNumber => ffi::GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER, 46 | Self::__Unknown(value) => value, 47 | } 48 | } 49 | } 50 | 51 | #[doc(hidden)] 52 | impl FromGlib for Edge { 53 | #[inline] 54 | unsafe fn from_glib(value: ffi::GtkLayerShellEdge) -> Self { 55 | skip_assert_initialized!(); 56 | 57 | match value { 58 | ffi::GTK_LAYER_SHELL_EDGE_LEFT => Self::Left, 59 | ffi::GTK_LAYER_SHELL_EDGE_RIGHT => Self::Right, 60 | ffi::GTK_LAYER_SHELL_EDGE_TOP => Self::Top, 61 | ffi::GTK_LAYER_SHELL_EDGE_BOTTOM => Self::Bottom, 62 | ffi::GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER => Self::EntryNumber, 63 | value => Self::__Unknown(value), 64 | } 65 | } 66 | } 67 | 68 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 69 | #[derive(Clone, Copy)] 70 | #[non_exhaustive] 71 | #[doc(alias = "GtkLayerShellKeyboardMode")] 72 | pub enum KeyboardMode { 73 | /// This window should not receive keyboard events. 74 | #[doc(alias = "GTK_LAYER_SHELL_KEYBOARD_MODE_NONE")] 75 | None, 76 | /// This window should have exclusive focus if it is on the top or overlay layer. 77 | #[doc(alias = "GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE")] 78 | Exclusive, 79 | /// The user should be able to focus and unfocues this window in an implementation 80 | /// defined way. Not supported for protocol version < 4. 81 | #[doc(alias = "GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND")] 82 | OnDemand, 83 | /// Should not be used except to get the number of entries. (NOTE: may change in 84 | /// future releases as more entries are added) 85 | #[doc(alias = "GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER")] 86 | EntryNumber, 87 | #[doc(hidden)] 88 | __Unknown(i32), 89 | } 90 | 91 | #[doc(hidden)] 92 | impl IntoGlib for KeyboardMode { 93 | type GlibType = ffi::GtkLayerShellKeyboardMode; 94 | 95 | #[inline] 96 | fn into_glib(self) -> ffi::GtkLayerShellKeyboardMode { 97 | match self { 98 | Self::None => ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_NONE, 99 | Self::Exclusive => ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE, 100 | Self::OnDemand => ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND, 101 | Self::EntryNumber => ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER, 102 | Self::__Unknown(value) => value, 103 | } 104 | } 105 | } 106 | 107 | #[doc(hidden)] 108 | impl FromGlib for KeyboardMode { 109 | #[inline] 110 | unsafe fn from_glib(value: ffi::GtkLayerShellKeyboardMode) -> Self { 111 | skip_assert_initialized!(); 112 | 113 | match value { 114 | ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_NONE => Self::None, 115 | ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE => Self::Exclusive, 116 | ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND => Self::OnDemand, 117 | ffi::GTK_LAYER_SHELL_KEYBOARD_MODE_ENTRY_NUMBER => Self::EntryNumber, 118 | value => Self::__Unknown(value), 119 | } 120 | } 121 | } 122 | 123 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 124 | #[derive(Clone, Copy)] 125 | #[non_exhaustive] 126 | #[doc(alias = "GtkLayerShellLayer")] 127 | pub enum Layer { 128 | /// The background layer. 129 | #[doc(alias = "GTK_LAYER_SHELL_LAYER_BACKGROUND")] 130 | Background, 131 | /// The bottom layer. 132 | #[doc(alias = "GTK_LAYER_SHELL_LAYER_BOTTOM")] 133 | Bottom, 134 | /// The top layer. 135 | #[doc(alias = "GTK_LAYER_SHELL_LAYER_TOP")] 136 | Top, 137 | /// The overlay layer. 138 | #[doc(alias = "GTK_LAYER_SHELL_LAYER_OVERLAY")] 139 | Overlay, 140 | /// Should not be used except to get the number of entries. (NOTE: may change in 141 | /// future releases as more entries are added) 142 | #[doc(alias = "GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER")] 143 | EntryNumber, 144 | #[doc(hidden)] 145 | __Unknown(i32), 146 | } 147 | 148 | #[doc(hidden)] 149 | impl IntoGlib for Layer { 150 | type GlibType = ffi::GtkLayerShellLayer; 151 | 152 | #[inline] 153 | fn into_glib(self) -> ffi::GtkLayerShellLayer { 154 | match self { 155 | Self::Background => ffi::GTK_LAYER_SHELL_LAYER_BACKGROUND, 156 | Self::Bottom => ffi::GTK_LAYER_SHELL_LAYER_BOTTOM, 157 | Self::Top => ffi::GTK_LAYER_SHELL_LAYER_TOP, 158 | Self::Overlay => ffi::GTK_LAYER_SHELL_LAYER_OVERLAY, 159 | Self::EntryNumber => ffi::GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER, 160 | Self::__Unknown(value) => value, 161 | } 162 | } 163 | } 164 | 165 | #[doc(hidden)] 166 | impl FromGlib for Layer { 167 | #[inline] 168 | unsafe fn from_glib(value: ffi::GtkLayerShellLayer) -> Self { 169 | skip_assert_initialized!(); 170 | 171 | match value { 172 | ffi::GTK_LAYER_SHELL_LAYER_BACKGROUND => Self::Background, 173 | ffi::GTK_LAYER_SHELL_LAYER_BOTTOM => Self::Bottom, 174 | ffi::GTK_LAYER_SHELL_LAYER_TOP => Self::Top, 175 | ffi::GTK_LAYER_SHELL_LAYER_OVERLAY => Self::Overlay, 176 | ffi::GTK_LAYER_SHELL_LAYER_ENTRY_NUMBER => Self::EntryNumber, 177 | value => Self::__Unknown(value), 178 | } 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/auto/functions.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | use crate::{ffi,Edge,KeyboardMode,Layer}; 7 | use glib::{prelude::*,translate::*}; 8 | 9 | 10 | /// When auto exclusive zone is enabled, exclusive zone is automatically set to the 11 | /// size of the `window` + relevant margin. To disable auto exclusive zone, just set the 12 | /// exclusive zone to 0 or any other fixed value. 13 | /// 14 | /// NOTE: you can control the auto exclusive zone by changing the margin on the non-anchored 15 | /// edge. This behavior is specific to gtk4-layer-shell and not part of the underlying protocol 16 | /// ## `window` 17 | /// A layer surface. 18 | #[doc(alias = "gtk_layer_auto_exclusive_zone_enable")] 19 | pub fn auto_exclusive_zone_enable(window: &impl IsA) { 20 | assert_initialized_main_thread!(); 21 | unsafe { 22 | ffi::gtk_layer_auto_exclusive_zone_enable(window.as_ref().to_glib_none().0); 23 | } 24 | } 25 | 26 | /// ## `window` 27 | /// A layer surface. 28 | /// 29 | /// # Returns 30 | /// 31 | /// if the surface's exclusive zone is set to change based on the window's size 32 | #[doc(alias = "gtk_layer_auto_exclusive_zone_is_enabled")] 33 | pub fn auto_exclusive_zone_is_enabled(window: &impl IsA) -> bool { 34 | assert_initialized_main_thread!(); 35 | unsafe { 36 | from_glib(ffi::gtk_layer_auto_exclusive_zone_is_enabled(window.as_ref().to_glib_none().0)) 37 | } 38 | } 39 | 40 | /// ## `window` 41 | /// A layer surface. 42 | /// ## `edge` 43 | /// the edge to which the surface may or may not be anchored 44 | /// 45 | /// # Returns 46 | /// 47 | /// if this surface is anchored to the given edge. 48 | #[doc(alias = "gtk_layer_get_anchor")] 49 | #[doc(alias = "get_anchor")] 50 | pub fn is_anchor(window: &impl IsA, edge: Edge) -> bool { 51 | assert_initialized_main_thread!(); 52 | unsafe { 53 | from_glib(ffi::gtk_layer_get_anchor(window.as_ref().to_glib_none().0, edge.into_glib())) 54 | } 55 | } 56 | 57 | /// ## `window` 58 | /// A layer surface. 59 | /// 60 | /// # Returns 61 | /// 62 | /// the window's exclusive zone(which may have been set manually or automatically) 63 | #[doc(alias = "gtk_layer_get_exclusive_zone")] 64 | #[doc(alias = "get_exclusive_zone")] 65 | pub fn exclusive_zone(window: &impl IsA) -> i32 { 66 | assert_initialized_main_thread!(); 67 | unsafe { 68 | ffi::gtk_layer_get_exclusive_zone(window.as_ref().to_glib_none().0) 69 | } 70 | } 71 | 72 | /// ## `window` 73 | /// A layer surface. 74 | /// 75 | /// # Returns 76 | /// 77 | /// current keyboard interactivity mode for `window`. 78 | #[doc(alias = "gtk_layer_get_keyboard_mode")] 79 | #[doc(alias = "get_keyboard_mode")] 80 | pub fn keyboard_mode(window: &impl IsA) -> KeyboardMode { 81 | assert_initialized_main_thread!(); 82 | unsafe { 83 | from_glib(ffi::gtk_layer_get_keyboard_mode(window.as_ref().to_glib_none().0)) 84 | } 85 | } 86 | 87 | /// ## `window` 88 | /// A layer surface. 89 | /// 90 | /// # Returns 91 | /// 92 | /// the current layer. 93 | #[doc(alias = "gtk_layer_get_layer")] 94 | #[doc(alias = "get_layer")] 95 | pub fn layer(window: &impl IsA) -> Option { 96 | assert_initialized_main_thread!(); 97 | unsafe { 98 | from_glib(ffi::gtk_layer_get_layer(window.as_ref().to_glib_none().0)) 99 | } 100 | } 101 | 102 | /// 103 | /// # Returns 104 | /// 105 | /// the major version number of the GTK Layer Shell library 106 | #[doc(alias = "gtk_layer_get_major_version")] 107 | #[doc(alias = "get_major_version")] 108 | pub fn major_version() -> u32 { 109 | assert_initialized_main_thread!(); 110 | unsafe { 111 | ffi::gtk_layer_get_major_version() 112 | } 113 | } 114 | 115 | /// ## `window` 116 | /// A layer surface. 117 | /// ## `edge` 118 | /// the margin edge to get 119 | /// 120 | /// # Returns 121 | /// 122 | /// the size of the margin for the given edge. 123 | #[doc(alias = "gtk_layer_get_margin")] 124 | #[doc(alias = "get_margin")] 125 | pub fn margin(window: &impl IsA, edge: Edge) -> i32 { 126 | assert_initialized_main_thread!(); 127 | unsafe { 128 | ffi::gtk_layer_get_margin(window.as_ref().to_glib_none().0, edge.into_glib()) 129 | } 130 | } 131 | 132 | /// 133 | /// # Returns 134 | /// 135 | /// the micro/patch version number of the GTK Layer Shell library 136 | #[doc(alias = "gtk_layer_get_micro_version")] 137 | #[doc(alias = "get_micro_version")] 138 | pub fn micro_version() -> u32 { 139 | assert_initialized_main_thread!(); 140 | unsafe { 141 | ffi::gtk_layer_get_micro_version() 142 | } 143 | } 144 | 145 | /// 146 | /// # Returns 147 | /// 148 | /// the minor version number of the GTK Layer Shell library 149 | #[doc(alias = "gtk_layer_get_minor_version")] 150 | #[doc(alias = "get_minor_version")] 151 | pub fn minor_version() -> u32 { 152 | assert_initialized_main_thread!(); 153 | unsafe { 154 | ffi::gtk_layer_get_minor_version() 155 | } 156 | } 157 | 158 | /// NOTE: To get which monitor the surface is actually on, use 159 | /// `gdk_display_get_monitor_at_window()`. 160 | /// ## `window` 161 | /// A layer surface. 162 | /// 163 | /// # Returns 164 | /// 165 | /// the monitor this surface will/has requested to be on. 166 | #[doc(alias = "gtk_layer_get_monitor")] 167 | #[doc(alias = "get_monitor")] 168 | pub fn monitor(window: &impl IsA) -> Option { 169 | assert_initialized_main_thread!(); 170 | unsafe { 171 | from_glib_none(ffi::gtk_layer_get_monitor(window.as_ref().to_glib_none().0)) 172 | } 173 | } 174 | 175 | /// NOTE: this function does not return ownership of the string. Do not free the returned string. 176 | /// Future calls into the library may invalidate the returned string. 177 | /// ## `window` 178 | /// A layer surface. 179 | /// 180 | /// # Returns 181 | /// 182 | /// a reference to the namespace property. If namespace is unset, returns the 183 | /// default namespace("gtk4-layer-shell"). Never returns [`None`]. 184 | #[doc(alias = "gtk_layer_get_namespace")] 185 | #[doc(alias = "get_namespace")] 186 | pub fn namespace(window: &impl IsA) -> Option { 187 | assert_initialized_main_thread!(); 188 | unsafe { 189 | from_glib_none(ffi::gtk_layer_get_namespace(window.as_ref().to_glib_none().0)) 190 | } 191 | } 192 | 193 | /// May block for a Wayland roundtrip the first time it's called. 194 | /// 195 | /// # Returns 196 | /// 197 | /// version of the zwlr_layer_shell_v1 protocol supported by the 198 | /// compositor or 0 if the protocol is not supported. 199 | #[doc(alias = "gtk_layer_get_protocol_version")] 200 | #[doc(alias = "get_protocol_version")] 201 | pub fn protocol_version() -> u32 { 202 | assert_initialized_main_thread!(); 203 | unsafe { 204 | ffi::gtk_layer_get_protocol_version() 205 | } 206 | } 207 | 208 | //#[doc(alias = "gtk_layer_get_zwlr_layer_surface_v1")] 209 | //#[doc(alias = "get_zwlr_layer_surface_v1")] 210 | //pub fn zwlr_layer_surface_v1(window: &impl IsA) -> /*Unimplemented*/Option { 211 | // unsafe { TODO: call ffi:gtk_layer_get_zwlr_layer_surface_v1() } 212 | //} 213 | 214 | /// Set the `window` up to be a layer surface once it is mapped. this must be called before 215 | /// the `window` is realized. 216 | /// ## `window` 217 | /// A [`gtk::Window`][crate::gtk::Window] to be turned into a layer surface. 218 | #[doc(alias = "gtk_layer_init_for_window")] 219 | pub fn init_for_window(window: &impl IsA) { 220 | assert_initialized_main_thread!(); 221 | unsafe { 222 | ffi::gtk_layer_init_for_window(window.as_ref().to_glib_none().0); 223 | } 224 | } 225 | 226 | /// ## `window` 227 | /// A [`gtk::Window`][crate::gtk::Window] that may or may not have a layer surface. 228 | /// 229 | /// # Returns 230 | /// 231 | /// if `window` has been initialized as a layer surface. 232 | #[doc(alias = "gtk_layer_is_layer_window")] 233 | pub fn is_layer_window(window: &impl IsA) -> bool { 234 | assert_initialized_main_thread!(); 235 | unsafe { 236 | from_glib(ffi::gtk_layer_is_layer_window(window.as_ref().to_glib_none().0)) 237 | } 238 | } 239 | 240 | /// May block for a Wayland roundtrip the first time it's called. 241 | /// 242 | /// # Returns 243 | /// 244 | /// [`true`] if the platform is Wayland and Wayland compositor supports the 245 | /// zwlr_layer_shell_v1 protocol. 246 | #[doc(alias = "gtk_layer_is_supported")] 247 | pub fn is_supported() -> bool { 248 | assert_initialized_main_thread!(); 249 | unsafe { 250 | from_glib(ffi::gtk_layer_is_supported()) 251 | } 252 | } 253 | 254 | /// Set whether `window` should be anchored to `edge`. 255 | /// - If two perpendicular edges are anchored, the surface with be anchored to that corner 256 | /// - If two opposite edges are anchored, the window will be stretched across the screen in that direction 257 | /// 258 | /// Default is [`false`] for each [`Edge`][crate::Edge] 259 | /// ## `window` 260 | /// A layer surface. 261 | /// ## `edge` 262 | /// A [`Edge`][crate::Edge] this layer surface may be anchored to. 263 | /// ## `anchor_to_edge` 264 | /// Whether or not to anchor this layer surface to `edge`. 265 | #[doc(alias = "gtk_layer_set_anchor")] 266 | pub fn set_anchor(window: &impl IsA, edge: Edge, anchor_to_edge: bool) { 267 | assert_initialized_main_thread!(); 268 | unsafe { 269 | ffi::gtk_layer_set_anchor(window.as_ref().to_glib_none().0, edge.into_glib(), anchor_to_edge.into_glib()); 270 | } 271 | } 272 | 273 | /// Has no effect unless the surface is anchored to an edge. Requests that the compositor 274 | /// does not place other surfaces within the given exclusive zone of the anchored edge. 275 | /// For example, a panel can request to not be covered by maximized windows. See 276 | /// wlr-layer-shell-unstable-v1.xml for details. 277 | /// 278 | /// Default is 0 279 | /// ## `window` 280 | /// A layer surface. 281 | /// ## `exclusive_zone` 282 | /// The size of the exclusive zone. 283 | #[doc(alias = "gtk_layer_set_exclusive_zone")] 284 | pub fn set_exclusive_zone(window: &impl IsA, exclusive_zone: i32) { 285 | assert_initialized_main_thread!(); 286 | unsafe { 287 | ffi::gtk_layer_set_exclusive_zone(window.as_ref().to_glib_none().0, exclusive_zone); 288 | } 289 | } 290 | 291 | /// Sets if/when `window` should receive keyboard events from the compositor, see 292 | /// GtkLayerShellKeyboardMode for details. 293 | /// 294 | /// Default is [`KeyboardMode::None`][crate::KeyboardMode::None] 295 | /// ## `window` 296 | /// A layer surface. 297 | /// ## `mode` 298 | /// The type of keyboard interactivity requested. 299 | #[doc(alias = "gtk_layer_set_keyboard_mode")] 300 | pub fn set_keyboard_mode(window: &impl IsA, mode: KeyboardMode) { 301 | assert_initialized_main_thread!(); 302 | unsafe { 303 | ffi::gtk_layer_set_keyboard_mode(window.as_ref().to_glib_none().0, mode.into_glib()); 304 | } 305 | } 306 | 307 | /// Set the "layer" on which the surface appears(controls if it is over top of or below other surfaces). The layer may 308 | /// be changed on-the-fly in the current version of the layer shell protocol, but on compositors that only support an 309 | /// older version the `window` is remapped so the change can take effect. 310 | /// 311 | /// Default is [`Layer::Top`][crate::Layer::Top] 312 | /// ## `window` 313 | /// A layer surface. 314 | /// ## `layer` 315 | /// The layer on which this surface appears. 316 | #[doc(alias = "gtk_layer_set_layer")] 317 | pub fn set_layer(window: &impl IsA, layer: Layer) { 318 | assert_initialized_main_thread!(); 319 | unsafe { 320 | ffi::gtk_layer_set_layer(window.as_ref().to_glib_none().0, layer.into_glib()); 321 | } 322 | } 323 | 324 | /// Set the margin for a specific `edge` of a `window`. Effects both surface's distance from 325 | /// the edge and its exclusive zone size(if auto exclusive zone enabled). 326 | /// 327 | /// Default is 0 for each [`Edge`][crate::Edge] 328 | /// ## `window` 329 | /// A layer surface. 330 | /// ## `edge` 331 | /// The [`Edge`][crate::Edge] for which to set the margin. 332 | /// ## `margin_size` 333 | /// The margin for `edge` to be set. 334 | #[doc(alias = "gtk_layer_set_margin")] 335 | pub fn set_margin(window: &impl IsA, edge: Edge, margin_size: i32) { 336 | assert_initialized_main_thread!(); 337 | unsafe { 338 | ffi::gtk_layer_set_margin(window.as_ref().to_glib_none().0, edge.into_glib(), margin_size); 339 | } 340 | } 341 | 342 | /// Set the output for the window to be placed on, or [`None`] to let the compositor choose. 343 | /// If the window is currently mapped, it will get remapped so the change can take effect. 344 | /// 345 | /// Default is [`None`] 346 | /// ## `window` 347 | /// A layer surface. 348 | /// ## `monitor` 349 | /// The output this layer surface will be placed on ([`None`] to let the compositor decide). 350 | #[doc(alias = "gtk_layer_set_monitor")] 351 | pub fn set_monitor(window: &impl IsA, monitor: Option<&gdk::Monitor>) { 352 | assert_initialized_main_thread!(); 353 | unsafe { 354 | ffi::gtk_layer_set_monitor(window.as_ref().to_glib_none().0, monitor.to_glib_none().0); 355 | } 356 | } 357 | 358 | /// Set the "namespace" of the surface. 359 | /// 360 | /// No one is quite sure what this is for, but it probably should be something generic 361 | /// ("panel", "osk", etc). The `name_space` string is copied, and caller maintains 362 | /// ownership of original. If the window is currently mapped, it will get remapped so 363 | /// the change can take effect. 364 | /// 365 | /// Default is "gtk4-layer-shell" (which will be used if set to [`None`]) 366 | /// ## `window` 367 | /// A layer surface. 368 | /// ## `name_space` 369 | /// The namespace of this layer surface. 370 | #[doc(alias = "gtk_layer_set_namespace")] 371 | pub fn set_namespace(window: &impl IsA, name_space: Option<&str>) { 372 | assert_initialized_main_thread!(); 373 | unsafe { 374 | ffi::gtk_layer_set_namespace(window.as_ref().to_glib_none().0, name_space.to_glib_none().0); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/auto/mod.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | mod enums; 7 | pub use self::enums::Edge; 8 | pub use self::enums::KeyboardMode; 9 | pub use self::enums::Layer; 10 | 11 | pub(crate) mod functions; 12 | 13 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/auto/versions.txt: -------------------------------------------------------------------------------- 1 | Generated by gir (https://github.com/gtk-rs/gir @ 5ce24253f28c) 2 | from .. (@ 1ecf663bd4c6+) 3 | from ../gir-files (@ 07aa7d177618) 4 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | #![deny(warnings)] 3 | #![allow(rustdoc::redundant_explicit_links)] 4 | 5 | #[allow(unused_imports)] 6 | #[allow(clippy::single_component_path_imports)] 7 | use gtk; // Required for the documentation to build without warnings 8 | 9 | use glib::translate::{FromGlib, TryFromGlib}; 10 | use gtk::prelude::IsA; 11 | use gtk4_layer_shell_sys as ffi; 12 | 13 | impl TryFromGlib for Layer { 14 | type Error = glib::translate::GlibNoneError; 15 | 16 | unsafe fn try_from_glib(value: ffi::GtkLayerShellLayer) -> Result { 17 | let layer = unsafe { Self::from_glib(value) }; 18 | // If we got an unknown variant, return an error; otherwise, return the value. 19 | match layer { 20 | Layer::__Unknown(_) => Err(glib::translate::GlibNoneError), 21 | _ => Ok(layer), 22 | } 23 | } 24 | } 25 | 26 | macro_rules! assert_initialized_main_thread { 27 | () => { 28 | if !::gtk::is_initialized_main_thread() { 29 | if ::gtk::is_initialized() { 30 | panic!("GTK may only be used from the main thread."); 31 | } else { 32 | panic!("GTK has not been initialized. Call `gtk::init` first."); 33 | } 34 | } 35 | }; 36 | } 37 | 38 | /// No-op. 39 | macro_rules! skip_assert_initialized { 40 | () => {}; 41 | } 42 | 43 | mod auto; 44 | pub use auto::{ 45 | functions::is_supported, functions::major_version, functions::micro_version, 46 | functions::minor_version, functions::protocol_version, Edge, KeyboardMode, Layer, 47 | }; 48 | 49 | mod manual; 50 | 51 | pub trait LayerShell: IsA { 52 | /// When auto exclusive zone is enabled, exclusive zone is automatically set to the 53 | /// size of the `window` + relevant margin. To disable auto exclusive zone, just set the 54 | /// exclusive zone to 0 or any other fixed value. 55 | /// 56 | /// NOTE: you can control the auto exclusive zone by changing the margin on the non-anchored 57 | /// edge. This behavior is specific to gtk-layer-shell and not part of the underlying protocol 58 | /// ## `window` 59 | /// A layer surface. 60 | #[doc(alias = "gtk_layer_auto_exclusive_zone_enable")] 61 | fn auto_exclusive_zone_enable(&self) { 62 | crate::auto::functions::auto_exclusive_zone_enable(self); 63 | } 64 | 65 | /// ## `window` 66 | /// A layer surface. 67 | /// 68 | /// # Returns 69 | /// 70 | /// if the surface's exclusive zone is set to change based on the window's size 71 | #[doc(alias = "gtk_layer_auto_exclusive_zone_is_enabled")] 72 | fn auto_exclusive_zone_is_enabled(&self) -> bool { 73 | crate::auto::functions::auto_exclusive_zone_is_enabled(self) 74 | } 75 | 76 | /// ## `window` 77 | /// A layer surface. 78 | /// ## `edge` 79 | /// the edge to which the surface may or may not be anchored 80 | /// 81 | /// # Returns 82 | /// 83 | /// if this surface is anchored to the given edge. 84 | #[doc(alias = "gtk_layer_get_anchor")] 85 | #[doc(alias = "get_anchor")] 86 | fn is_anchor(&self, edge: Edge) -> bool { 87 | crate::auto::functions::is_anchor(self, edge) 88 | } 89 | 90 | /// ## `window` 91 | /// A layer surface. 92 | /// 93 | /// # Returns 94 | /// 95 | /// the window's exclusive zone (which may have been set manually or automatically) 96 | #[doc(alias = "gtk_layer_get_exclusive_zone")] 97 | #[doc(alias = "get_exclusive_zone")] 98 | fn exclusive_zone(&self) -> i32 { 99 | crate::auto::functions::exclusive_zone(self) 100 | } 101 | 102 | /// ## `window` 103 | /// A layer surface. 104 | /// 105 | /// # Returns 106 | /// 107 | /// current keyboard interactivity mode for `window`. 108 | #[doc(alias = "gtk_layer_get_keyboard_mode")] 109 | #[doc(alias = "get_keyboard_mode")] 110 | fn keyboard_mode(&self) -> KeyboardMode { 111 | crate::auto::functions::keyboard_mode(self) 112 | } 113 | 114 | /// ## `window` 115 | /// A layer surface. 116 | /// 117 | /// # Returns 118 | /// 119 | /// the current layer. 120 | #[doc(alias = "gtk_layer_get_layer")] 121 | #[doc(alias = "get_layer")] 122 | fn layer(&self) -> Option { 123 | crate::auto::functions::layer(self) 124 | } 125 | 126 | /// ## `window` 127 | /// A layer surface. 128 | /// ## `edge` 129 | /// the margin edge to get 130 | /// 131 | /// # Returns 132 | /// 133 | /// the size of the margin for the given edge. 134 | #[doc(alias = "gtk_layer_get_margin")] 135 | #[doc(alias = "get_margin")] 136 | fn margin(&self, edge: Edge) -> i32 { 137 | crate::auto::functions::margin(self, edge) 138 | } 139 | 140 | /// NOTE: To get which monitor the surface is actually on, use 141 | /// `gdk_display_get_monitor_at_window()`. 142 | /// ## `window` 143 | /// A layer surface. 144 | /// 145 | /// # Returns 146 | /// 147 | /// the monitor this surface will/has requested to be on, can be [`None`]. 148 | #[doc(alias = "gtk_layer_get_monitor")] 149 | #[doc(alias = "get_monitor")] 150 | fn monitor(&self) -> Option { 151 | crate::auto::functions::monitor(self) 152 | } 153 | 154 | /// NOTE: this function does not return ownership of the string. Do not free the returned string. 155 | /// Future calls into the library may invalidate the returned string. 156 | /// ## `window` 157 | /// A layer surface. 158 | /// 159 | /// # Returns 160 | /// 161 | /// a reference to the namespace property. If namespace is unset, returns the 162 | /// default namespace ("gtk-layer-shell"). Never returns [`None`]. 163 | #[doc(alias = "gtk_layer_get_namespace")] 164 | #[doc(alias = "get_namespace")] 165 | fn namespace(&self) -> Option { 166 | crate::auto::functions::namespace(self) 167 | } 168 | 169 | /// Set the `window` up to be a layer surface once it is mapped. this must be called before 170 | /// the `window` is realized. 171 | /// ## `window` 172 | /// A [`gtk::Window`][crate::gtk::Window] to be turned into a layer surface. 173 | #[doc(alias = "init_for_window")] 174 | #[doc(alias = "gtk_layer_init_for_window")] 175 | fn init_layer_shell(&self) { 176 | crate::auto::functions::init_for_window(self); 177 | } 178 | 179 | /// ## `window` 180 | /// A [`gtk::Window`][crate::gtk::Window] that may or may not have a layer surface. 181 | /// 182 | /// # Returns 183 | /// 184 | /// if `window` has been initialized as a layer surface. 185 | #[doc(alias = "gtk_layer_is_layer_window")] 186 | fn is_layer_window(&self) -> bool { 187 | crate::auto::functions::is_layer_window(self) 188 | } 189 | 190 | /// Set whether `window` should be anchored to `edge`. 191 | /// - If two perpendicular edges are anchored, the surface with be anchored to that corner 192 | /// - If two opposite edges are anchored, the window will be stretched across the screen in that direction 193 | /// 194 | /// Default is [`false`] for each [`Edge`][crate::Edge] 195 | /// ## `window` 196 | /// A layer surface. 197 | /// ## `edge` 198 | /// A [`Edge`][crate::Edge] this layer surface may be anchored to. 199 | /// ## `anchor_to_edge` 200 | /// Whether or not to anchor this layer surface to `edge`. 201 | #[doc(alias = "gtk_layer_set_anchor")] 202 | fn set_anchor(&self, edge: Edge, anchor_to_edge: bool) { 203 | crate::auto::functions::set_anchor(self, edge, anchor_to_edge); 204 | } 205 | 206 | /// Has no effect unless the surface is anchored to an edge. Requests that the compositor 207 | /// does not place other surfaces within the given exclusive zone of the anchored edge. 208 | /// For example, a panel can request to not be covered by maximized windows. See 209 | /// wlr-layer-shell-unstable-v1.xml for details. 210 | /// 211 | /// Default is 0 212 | /// ## `window` 213 | /// A layer surface. 214 | /// ## `exclusive_zone` 215 | /// The size of the exclusive zone. 216 | #[doc(alias = "gtk_layer_set_exclusive_zone")] 217 | fn set_exclusive_zone(&self, exclusive_zone: i32) { 218 | crate::auto::functions::set_exclusive_zone(self, exclusive_zone); 219 | } 220 | 221 | /// Sets if/when `window` should receive keyboard events from the compositor, see 222 | /// GtkLayerShellKeyboardMode for details. 223 | /// 224 | /// Default is [`KeyboardMode::None`][crate::KeyboardMode::None] 225 | /// ## `window` 226 | /// A layer surface. 227 | /// ## `mode` 228 | /// The type of keyboard interactivity requested. 229 | #[doc(alias = "gtk_layer_set_keyboard_mode")] 230 | fn set_keyboard_mode(&self, mode: KeyboardMode) { 231 | crate::auto::functions::set_keyboard_mode(self, mode); 232 | } 233 | 234 | /// Set the "layer" on which the surface appears (controls if it is over top of or below other surfaces). The layer may 235 | /// be changed on-the-fly in the current version of the layer shell protocol, but on compositors that only support an 236 | /// older version the `window` is remapped so the change can take effect. 237 | /// 238 | /// Default is [`Layer::Top`][crate::Layer::Top] 239 | /// ## `window` 240 | /// A layer surface. 241 | /// ## `layer` 242 | /// The layer on which this surface appears. 243 | #[doc(alias = "gtk_layer_set_layer")] 244 | fn set_layer(&self, layer: Layer) { 245 | crate::auto::functions::set_layer(self, layer); 246 | } 247 | 248 | /// Set the margin for a specific `edge` of a `window`. Effects both surface's distance from 249 | /// the edge and its exclusive zone size (if auto exclusive zone enabled). 250 | /// 251 | /// Default is 0 for each [`Edge`][crate::Edge] 252 | /// ## `window` 253 | /// A layer surface. 254 | /// ## `edge` 255 | /// The [`Edge`][crate::Edge] for which to set the margin. 256 | /// ## `margin_size` 257 | /// The margin for `edge` to be set. 258 | #[doc(alias = "gtk_layer_set_margin")] 259 | fn set_margin(&self, edge: Edge, margin_size: i32) { 260 | crate::auto::functions::set_margin(self, edge, margin_size); 261 | } 262 | 263 | /// Set the output for the window to be placed on, or [`None`] to let the compositor choose. 264 | /// If the window is currently mapped, it will get remapped so the change can take effect. 265 | /// 266 | /// Default is [`None`] 267 | /// ## `window` 268 | /// A layer surface. 269 | /// ## `monitor` 270 | /// The output this layer surface will be placed on ([`None`] to let the compositor decide). 271 | #[doc(alias = "gtk_layer_set_monitor")] 272 | fn set_monitor(&self, monitor: Option<&gdk::Monitor>) { 273 | crate::auto::functions::set_monitor(self, monitor); 274 | } 275 | 276 | /// Set the "namespace" of the surface. 277 | /// 278 | /// No one is quite sure what this is for, but it probably should be something generic 279 | /// ("panel", "osk", etc). The `name_space` string is copied, and caller maintains 280 | /// ownership of original. If the window is currently mapped, it will get remapped so 281 | /// the change can take effect. 282 | /// 283 | /// Default is "gtk-layer-shell" (which will be used if set to [`None`]) 284 | /// ## `window` 285 | /// A layer surface. 286 | /// ## `name_space` 287 | /// The namespace of this layer surface. 288 | #[doc(alias = "gtk_layer_set_namespace")] 289 | fn set_namespace(&self, name_space: Option<&str>) { 290 | crate::auto::functions::set_namespace(self, name_space); 291 | } 292 | 293 | /* TODO: Fix this 294 | /// ## `window` 295 | /// A layer surface. 296 | /// 297 | /// # Returns 298 | /// 299 | /// The underlying layer surface Wayland object 300 | /// 301 | #[doc(alias = "gtk_layer_get_zwlr_layer_surface_v1")] 302 | #[doc(alias = "get_zwlr_layer_surface_v1")] 303 | fn zwlr_layer_surface_v1( 304 | window: &impl glib::object::IsA, 305 | ) -> *mut ZwlrLayerSurfaceV1 { 306 | zwlr_layer_surface_v1(self) 307 | }*/ 308 | } 309 | 310 | // The default implementation is always fine 311 | impl> LayerShell for T {} 312 | -------------------------------------------------------------------------------- /gtk4-layer-shell/src/manual.rs: -------------------------------------------------------------------------------- 1 | /* 2 | pub type ZwlrLayerSurfaceV1 = ffi::zwlr_layer_surface_v1; 3 | 4 | /// ## `window` 5 | /// A layer surface. 6 | /// 7 | /// # Returns 8 | /// 9 | /// The underlying layer surface Wayland object 10 | #[doc(alias = "gtk_layer_get_zwlr_layer_surface_v1")] 11 | #[doc(alias = "get_zwlr_layer_surface_v1")] 12 | pub fn zwlr_layer_surface_v1( 13 | window: &impl glib::object::IsA, 14 | ) -> *mut ZwlrLayerSurfaceV1 { 15 | use glib::translate::ToGlibPtr; 16 | 17 | assert_initialized_main_thread!(); 18 | unsafe { ffi::gtk_layer_get_zwlr_layer_surface_v1(window.as_ref().to_glib_none().0) } 19 | } 20 | */ 21 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk4-session-lock-sys" 3 | version = "0.1.1" 4 | description = "Unsave gir-generated FFI bindings for gtk4-session-lock" 5 | repository = "https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock-sys" 6 | documentation = "https://docs.rs/gtk4-session-lock-sys/" 7 | keywords = ["gtk4", "gtk4-session-lock", "wayland", "FFI", "unsafe"] 8 | categories = ["external-ffi-bindings", "gui"] 9 | build = "build.rs" 10 | 11 | [package.authors] 12 | workspace = true 13 | 14 | [package.rust-version] 15 | workspace = true 16 | 17 | [package.edition] 18 | workspace = true 19 | 20 | [package.license] 21 | workspace = true 22 | 23 | [package.metadata.system-deps.gtk4_layer_shell_0] 24 | name = "gtk4-layer-shell-0" 25 | version = "1" 26 | 27 | [package.metadata.docs.rs] 28 | rustc-args = ["--cfg", "docsrs"] 29 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 30 | all-features = true 31 | 32 | [lib] 33 | name = "gtk4_session_lock_sys" 34 | 35 | [dependencies] 36 | libc = "0.2" 37 | 38 | [dependencies.glib-sys] 39 | workspace = true 40 | 41 | [dependencies.gdk4-sys] 42 | workspace = true 43 | 44 | [dependencies.gobject-sys] 45 | workspace = true 46 | 47 | [dependencies.gtk4-sys] 48 | workspace = true 49 | 50 | [build-dependencies] 51 | system-deps = "7" 52 | 53 | [dev-dependencies] 54 | shell-words = "1.0.0" 55 | tempfile = "3" 56 | 57 | [features] 58 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/Gir.toml: -------------------------------------------------------------------------------- 1 | [options] 2 | library = "Gtk4SessionLock" 3 | version = "1.0" 4 | target_path = "." 5 | min_cfg_version = "1.0" 6 | work_mode = "sys" 7 | girs_directories = ["../gir-files", ".."] 8 | single_version_file = true 9 | 10 | external_libraries = ["GLib", "GObject"] 11 | 12 | [external_libraries] 13 | gdk4 = "Gdk" 14 | gtk4 = "Gtk" 15 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/README.md: -------------------------------------------------------------------------------- 1 | [![Crate](https://img.shields.io/crates/v/gtk4-session-lock-sys.svg)](https://crates.io/crates/gtk4-session-lock-sys) 2 | [![docs.rs](https://docs.rs/gtk4-session-lock-sys/badge.svg)](https://docs.rs/gtk4-session-lock-sys) 3 | [![dependency status](https://deps.rs/crate/gtk4-session-lock-sys/0.1.1/status.svg)](https://deps.rs/crate/gtk4-session-lock-sys/0.1.1) 4 | 5 | [![maintenance-status: passively-maintained (as of 2025-03-13)](https://img.shields.io/badge/maintenance--status-passively--maintained_%28as_of_2025--03--13%29-forestgreen)](https://gist.github.com/rusty-snake/574a91f1df9f97ec77ca308d6d731e29) 6 | ![dependabot status](https://img.shields.io/badge/dependabot-enabled-025e8c?logo=Dependabot) 7 | [![Build](https://img.shields.io/github/actions/workflow/status/pentamassiv/gtk4-layer-shell-gir/build.yaml?branch=main)](https://github.com/pentamassiv/gtk4-layer-shell-gir/actions/workflows/build.yaml) 8 | 9 | 10 | # gtk4-session-lock-sys 11 | These are the unsafe FFI bindings for [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock). You likely want to use that crate instead. It allows building lock screens with GTK4. To do that, the [Session Lock](https://wayland.app/protocols/ext-session-lock-v1) Wayland protocol is used. A list of supported compositors can be found [here](https://wayland.app/protocols/ext-session-lock-v1#compositor-support). 12 | 13 | ## Dependencies 14 | You need to have `gtk4` and `gtk4-layer-shell` (the C library) installed on your system. `gtk4-layer-shell` version 1.1.0 or higher is needed to use `gtk4-session-lock`. If you want to use [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell) and [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock) together in a project, make sure to use the same .so file of `gtk4-layer-shell` for both. 15 | 16 | If your distribution does not provide a current enough version of `gtk4-layer-shell`, you can [build it from source](https://github.com/wmww/gtk4-layer-shell#building-from-source). If you did that, you might also have to set the following two environment variables: 17 | ```bash 18 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 19 | export LD_LIBRARY_PATH=/usr/local/lib 20 | ``` 21 | 22 | ## Generating the bindings 23 | Generating the bindings yourself is not necessary to be able to use them. You can just use the version published on crates.io. If you want to do it anyways, you can find a description [here](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/generate_bindings.md). 24 | 25 | ## Maintenance status 26 | This crate is just an unsafe wrapper for the C library so the bindings are feature complete and not actively worked on. The C library is actively developed and I keep the bindings up-to-date with it. If you encounter any problems, feel free to open a PR. 27 | 28 | ## Contributing 29 | Pull requests are very welcome but please keep the maintenance status in mind. 30 | 31 | ## License 32 | [MIT](https://choosealicense.com/licenses/mit/) 33 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/build.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #[cfg(not(docsrs))] 7 | use std::process; 8 | 9 | #[cfg(docsrs)] 10 | fn main() {} // prevent linking libraries to avoid documentation failure 11 | 12 | #[cfg(not(docsrs))] 13 | fn main() { 14 | if let Err(s) = system_deps::Config::new().probe() { 15 | println!("cargo:warning={s}"); 16 | process::exit(1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/src/auto/versions.txt: -------------------------------------------------------------------------------- 1 | Generated by gir (https://github.com/gtk-rs/gir @ 3491754499c3) 2 | from .. (@ 3a74b579f31b) 3 | from ../gir-files (@ 07aa7d177618) 4 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] 7 | #![allow(clippy::approx_constant, clippy::type_complexity, clippy::unreadable_literal, clippy::upper_case_acronyms)] 8 | #![cfg_attr(docsrs, feature(doc_cfg))] 9 | 10 | use glib_sys as glib; 11 | use gobject_sys as gobject; 12 | use gdk4_sys as gdk; 13 | use gtk4_sys as gtk; 14 | 15 | #[allow(unused_imports)] 16 | use std::ffi::{c_int, c_char, c_uchar, c_float, c_uint, c_double, 17 | c_short, c_ushort, c_long, c_ulong, c_void}; 18 | #[allow(unused_imports)] 19 | use libc::{size_t, ssize_t, time_t, off_t, intptr_t, uintptr_t, FILE}; 20 | #[cfg(unix)] 21 | #[allow(unused_imports)] 22 | use libc::{dev_t, gid_t, pid_t, socklen_t, uid_t}; 23 | 24 | #[allow(unused_imports)] 25 | use glib::{gboolean, gconstpointer, gpointer, GType}; 26 | 27 | // Records 28 | #[derive(Copy, Clone)] 29 | #[repr(C)] 30 | pub struct GtkSessionLockInstanceClass { 31 | pub parent_class: gobject::GObjectClass, 32 | } 33 | 34 | impl ::std::fmt::Debug for GtkSessionLockInstanceClass { 35 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 36 | f.debug_struct(&format!("GtkSessionLockInstanceClass @ {self:p}")) 37 | .field("parent_class", &self.parent_class) 38 | .finish() 39 | } 40 | } 41 | 42 | // Classes 43 | #[repr(C)] 44 | #[allow(dead_code)] 45 | pub struct GtkSessionLockInstance { 46 | _data: [u8; 0], 47 | _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, 48 | } 49 | 50 | impl ::std::fmt::Debug for GtkSessionLockInstance { 51 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 52 | f.debug_struct(&format!("GtkSessionLockInstance @ {self:p}")) 53 | .finish() 54 | } 55 | } 56 | 57 | extern "C" { 58 | 59 | //========================================================================= 60 | // GtkSessionLockInstance 61 | //========================================================================= 62 | pub fn gtk_session_lock_instance_get_type() -> GType; 63 | pub fn gtk_session_lock_instance_new() -> *mut GtkSessionLockInstance; 64 | pub fn gtk_session_lock_instance_assign_window_to_monitor(self_: *mut GtkSessionLockInstance, window: *mut gtk::GtkWindow, monitor: *mut gdk::GdkMonitor); 65 | pub fn gtk_session_lock_instance_is_locked(self_: *mut GtkSessionLockInstance) -> gboolean; 66 | pub fn gtk_session_lock_instance_lock(self_: *mut GtkSessionLockInstance) -> gboolean; 67 | pub fn gtk_session_lock_instance_unlock(self_: *mut GtkSessionLockInstance); 68 | 69 | //========================================================================= 70 | // Other functions 71 | //========================================================================= 72 | pub fn gtk_session_lock_is_supported() -> gboolean; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/tests/abi.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #![cfg(unix)] 7 | 8 | use gtk4_session_lock_sys::*; 9 | use std::mem::{align_of, size_of}; 10 | use std::env; 11 | use std::error::Error; 12 | use std::ffi::OsString; 13 | use std::path::Path; 14 | use std::process::{Command, Stdio}; 15 | use std::str; 16 | use tempfile::Builder; 17 | 18 | static PACKAGES: &[&str] = &["gtk4-layer-shell-0"]; 19 | 20 | #[derive(Clone, Debug)] 21 | struct Compiler { 22 | pub args: Vec, 23 | } 24 | 25 | impl Compiler { 26 | pub fn new() -> Result> { 27 | let mut args = get_var("CC", "cc")?; 28 | args.push("-Wno-deprecated-declarations".to_owned()); 29 | // For _Generic 30 | args.push("-std=c11".to_owned()); 31 | // For %z support in printf when using MinGW. 32 | args.push("-D__USE_MINGW_ANSI_STDIO".to_owned()); 33 | args.extend(get_var("CFLAGS", "")?); 34 | args.extend(get_var("CPPFLAGS", "")?); 35 | args.extend(pkg_config_cflags(PACKAGES)?); 36 | Ok(Self { args }) 37 | } 38 | 39 | pub fn compile(&self, src: &Path, out: &Path) -> Result<(), Box> { 40 | let mut cmd = self.to_command(); 41 | cmd.arg(src); 42 | cmd.arg("-o"); 43 | cmd.arg(out); 44 | let status = cmd.spawn()?.wait()?; 45 | if !status.success() { 46 | return Err(format!("compilation command {cmd:?} failed, {status}").into()); 47 | } 48 | Ok(()) 49 | } 50 | 51 | fn to_command(&self) -> Command { 52 | let mut cmd = Command::new(&self.args[0]); 53 | cmd.args(&self.args[1..]); 54 | cmd 55 | } 56 | } 57 | 58 | fn get_var(name: &str, default: &str) -> Result, Box> { 59 | match env::var(name) { 60 | Ok(value) => Ok(shell_words::split(&value)?), 61 | Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?), 62 | Err(err) => Err(format!("{name} {err}").into()), 63 | } 64 | } 65 | 66 | fn pkg_config_cflags(packages: &[&str]) -> Result, Box> { 67 | if packages.is_empty() { 68 | return Ok(Vec::new()); 69 | } 70 | let pkg_config = env::var_os("PKG_CONFIG") 71 | .unwrap_or_else(|| OsString::from("pkg-config")); 72 | let mut cmd = Command::new(pkg_config); 73 | cmd.arg("--cflags"); 74 | cmd.args(packages); 75 | cmd.stderr(Stdio::inherit()); 76 | let out = cmd.output()?; 77 | if !out.status.success() { 78 | let (status, stdout) = (out.status, String::from_utf8_lossy(&out.stdout)); 79 | return Err(format!("command {cmd:?} failed, {status:?}\nstdout: {stdout}").into()); 80 | } 81 | let stdout = str::from_utf8(&out.stdout)?; 82 | Ok(shell_words::split(stdout.trim())?) 83 | } 84 | 85 | 86 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 87 | struct Layout { 88 | size: usize, 89 | alignment: usize, 90 | } 91 | 92 | #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 93 | struct Results { 94 | /// Number of successfully completed tests. 95 | passed: usize, 96 | /// Total number of failed tests (including those that failed to compile). 97 | failed: usize, 98 | } 99 | 100 | impl Results { 101 | fn record_passed(&mut self) { 102 | self.passed += 1; 103 | } 104 | fn record_failed(&mut self) { 105 | self.failed += 1; 106 | } 107 | fn summary(&self) -> String { 108 | format!("{} passed; {} failed", self.passed, self.failed) 109 | } 110 | fn expect_total_success(&self) { 111 | if self.failed == 0 { 112 | println!("OK: {}", self.summary()); 113 | } else { 114 | panic!("FAILED: {}", self.summary()); 115 | }; 116 | } 117 | } 118 | 119 | #[test] 120 | fn cross_validate_constants_with_c() { 121 | let mut c_constants: Vec<(String, String)> = Vec::new(); 122 | 123 | for l in get_c_output("constant").unwrap().lines() { 124 | let (name, value) = l.split_once(';').expect("Missing ';' separator"); 125 | c_constants.push((name.to_owned(), value.to_owned())); 126 | } 127 | 128 | let mut results = Results::default(); 129 | 130 | for ((rust_name, rust_value), (c_name, c_value)) in 131 | RUST_CONSTANTS.iter().zip(c_constants.iter()) 132 | { 133 | if rust_name != c_name { 134 | results.record_failed(); 135 | eprintln!("Name mismatch:\nRust: {rust_name:?}\nC: {c_name:?}"); 136 | continue; 137 | } 138 | 139 | if rust_value != c_value { 140 | results.record_failed(); 141 | eprintln!( 142 | "Constant value mismatch for {rust_name}\nRust: {rust_value:?}\nC: {c_value:?}", 143 | ); 144 | continue; 145 | } 146 | 147 | results.record_passed(); 148 | } 149 | 150 | results.expect_total_success(); 151 | } 152 | 153 | #[test] 154 | fn cross_validate_layout_with_c() { 155 | let mut c_layouts = Vec::new(); 156 | 157 | for l in get_c_output("layout").unwrap().lines() { 158 | let (name, value) = l.split_once(';').expect("Missing first ';' separator"); 159 | let (size, alignment) = value.split_once(';').expect("Missing second ';' separator"); 160 | let size = size.parse().expect("Failed to parse size"); 161 | let alignment = alignment.parse().expect("Failed to parse alignment"); 162 | c_layouts.push((name.to_owned(), Layout { size, alignment })); 163 | } 164 | 165 | let mut results = Results::default(); 166 | 167 | for ((rust_name, rust_layout), (c_name, c_layout)) in 168 | RUST_LAYOUTS.iter().zip(c_layouts.iter()) 169 | { 170 | if rust_name != c_name { 171 | results.record_failed(); 172 | eprintln!("Name mismatch:\nRust: {rust_name:?}\nC: {c_name:?}"); 173 | continue; 174 | } 175 | 176 | if rust_layout != c_layout { 177 | results.record_failed(); 178 | eprintln!( 179 | "Layout mismatch for {rust_name}\nRust: {rust_layout:?}\nC: {c_layout:?}", 180 | ); 181 | continue; 182 | } 183 | 184 | results.record_passed(); 185 | } 186 | 187 | results.expect_total_success(); 188 | } 189 | 190 | fn get_c_output(name: &str) -> Result> { 191 | let tmpdir = Builder::new().prefix("abi").tempdir()?; 192 | let exe = tmpdir.path().join(name); 193 | let c_file = Path::new("tests").join(name).with_extension("c"); 194 | 195 | let cc = Compiler::new().expect("configured compiler"); 196 | cc.compile(&c_file, &exe)?; 197 | 198 | let mut cmd = Command::new(exe); 199 | cmd.stderr(Stdio::inherit()); 200 | let out = cmd.output()?; 201 | if !out.status.success() { 202 | let (status, stdout) = (out.status, String::from_utf8_lossy(&out.stdout)); 203 | return Err(format!("command {cmd:?} failed, {status:?}\nstdout: {stdout}").into()); 204 | } 205 | 206 | Ok(String::from_utf8(out.stdout)?) 207 | } 208 | 209 | const RUST_LAYOUTS: &[(&str, Layout)] = &[ 210 | ("GtkSessionLockInstanceClass", Layout {size: size_of::(), alignment: align_of::()}), 211 | ]; 212 | 213 | const RUST_CONSTANTS: &[(&str, &str)] = &[ 214 | ]; 215 | 216 | 217 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/tests/constant.c: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #include "manual.h" 7 | #include 8 | 9 | #define PRINT_CONSTANT(CONSTANT_NAME) \ 10 | printf("%s;", #CONSTANT_NAME); \ 11 | printf(_Generic((CONSTANT_NAME), \ 12 | char *: "%s", \ 13 | const char *: "%s", \ 14 | char: "%c", \ 15 | signed char: "%hhd", \ 16 | unsigned char: "%hhu", \ 17 | short int: "%hd", \ 18 | unsigned short int: "%hu", \ 19 | int: "%d", \ 20 | unsigned int: "%u", \ 21 | long: "%ld", \ 22 | unsigned long: "%lu", \ 23 | long long: "%lld", \ 24 | unsigned long long: "%llu", \ 25 | float: "%f", \ 26 | double: "%f", \ 27 | long double: "%ld"), \ 28 | CONSTANT_NAME); \ 29 | printf("\n"); 30 | 31 | int main() { 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/tests/layout.c: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | #include "manual.h" 7 | #include 8 | #include 9 | 10 | int main() { 11 | printf("%s;%zu;%zu\n", "GtkSessionLockInstanceClass", sizeof(GtkSessionLockInstanceClass), alignof(GtkSessionLockInstanceClass)); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /gtk4-session-lock-sys/tests/manual.h: -------------------------------------------------------------------------------- 1 | // Feel free to edit this file, it won't be regenerated by gir generator unless removed. 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /gtk4-session-lock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk4-session-lock" 3 | version = "0.1.2" 4 | description = "Save gir-generated wrapper for gtk4-session-lock" 5 | repository = "https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock" 6 | documentation = "https://docs.rs/gtk4-session-lock/" 7 | keywords = ["gtk4", "gtk4-session-lock", "wayland", "gir", "wrapper"] 8 | categories = ["api-bindings", "gui"] 9 | exclude = ["examples"] 10 | authors.workspace = true 11 | rust-version.workspace = true 12 | edition.workspace = true 13 | license.workspace = true 14 | 15 | [package.metadata.docs.rs] 16 | all-features = true 17 | rustc-args = ["--cfg", "docsrs"] 18 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 19 | 20 | [features] 21 | 22 | [dependencies] 23 | libc.workspace = true 24 | glib.workspace = true 25 | glib-sys.workspace = true 26 | gdk.workspace = true 27 | gtk.workspace = true 28 | gtk4-session-lock-sys.workspace = true 29 | 30 | [dev-dependencies] 31 | gio.workspace = true 32 | 33 | [target.'cfg(docsrs)'.dependencies] 34 | gdk = { package = "gdk4", version = "0.9", features = ["gl", "v4_16"] } 35 | -------------------------------------------------------------------------------- /gtk4-session-lock/Gir.toml: -------------------------------------------------------------------------------- 1 | [options] 2 | library = "Gtk4SessionLock" 3 | version = "1.0" 4 | target_path = "." 5 | min_cfg_version = "1.0" 6 | work_mode = "normal" 7 | girs_directories = ["../gir-files", ".."] 8 | generate_safety_asserts = true 9 | deprecate_by_min_version = true 10 | single_version_file = true 11 | 12 | manual = ["Gtk.Window", "Gdk.Monitor"] 13 | 14 | generate = ["Gtk4SessionLock.Instance"] 15 | 16 | [[object]] 17 | name = "Gtk4SessionLock.*" 18 | status = "ignore" 19 | [[object.function]] 20 | name = "is_supported" 21 | manual = true 22 | -------------------------------------------------------------------------------- /gtk4-session-lock/README.md: -------------------------------------------------------------------------------- 1 | [![Crate](https://img.shields.io/crates/v/gtk4-session-lock.svg)](https://crates.io/crates/gtk4-session-lock) 2 | [![docs.rs](https://docs.rs/gtk4-session-lock/badge.svg)](https://docs.rs/gtk4-session-lock) 3 | [![dependency status](https://deps.rs/crate/gtk4-session-lock/0.1.2/status.svg)](https://deps.rs/crate/gtk4-session-lock/0.1.2) 4 | 5 | [![maintenance-status: passively-maintained (as of 2025-03-13)](https://img.shields.io/badge/maintenance--status-passively--maintained_%28as_of_2025--03--13%29-forestgreen)](https://gist.github.com/rusty-snake/574a91f1df9f97ec77ca308d6d731e29) 6 | ![dependabot status](https://img.shields.io/badge/dependabot-enabled-025e8c?logo=Dependabot) 7 | [![Build](https://img.shields.io/github/actions/workflow/status/pentamassiv/gtk4-layer-shell-gir/build.yaml?branch=main)](https://github.com/pentamassiv/gtk4-layer-shell-gir/actions/workflows/build.yaml) 8 | 9 | # gtk4-session-lock 10 | This crate allows building lock screens with GTK4. To do that, the [Session Lock](https://wayland.app/protocols/ext-session-lock-v1) Wayland protocol is used. A list of supported compositors can be found [here](https://wayland.app/protocols/ext-session-lock-v1#compositor-support). 11 | 12 | ## Dependencies 13 | You need to have `gtk4` and `gtk4-layer-shell` (the C library) installed on your system. `gtk4-layer-shell` version 1.1.0 or higher is needed to use `gtk4-session-lock`. If you want to use [gtk4-layer-shell](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-layer-shell) and [gtk4-session-lock](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock) together in a project, make sure to use the same .so file of `gtk4-layer-shell` for both. 14 | 15 | If your distribution does not provide a current enough version of `gtk4-layer-shell`, you can [build it from source](https://github.com/wmww/gtk4-layer-shell#building-from-source). If you did that, you might also have to set the following two environment variables: 16 | ```bash 17 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 18 | export LD_LIBRARY_PATH=/usr/local/lib 19 | ``` 20 | 21 | ## Usage 22 | Have a look at the [simple example](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/gtk4-session-lock/examples/simple.rs) to see how the bindings can be used. It works analogous to the original. 23 | 24 | ## Generating the wrapper 25 | Generating the bindings yourself is not necessary to be able to use them. You can just use the version published on crates.io. If you want to do it anyways, you can find a description [here](https://github.com/pentamassiv/gtk4-layer-shell-gir/tree/main/generate_bindings.md). 26 | 27 | ## Maintenance status 28 | This crate is just a safe wrapper for the C library so the bindings are feature complete and not actively worked on. The C library is actively developed and I keep the bindings up-to-date with it. If you encounter any problems, feel free to open a PR. 29 | 30 | ## Contributing 31 | Pull requests are very welcome but please keep the maintenance status in mind. 32 | 33 | ## License 34 | [MIT](https://choosealicense.com/licenses/mit/) 35 | -------------------------------------------------------------------------------- /gtk4-session-lock/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use gio::prelude::*; 2 | use glib::clone; 3 | use gtk::prelude::*; 4 | use gtk4_session_lock::Instance as SessionLockInstance; 5 | 6 | fn main() { 7 | if !gtk4_session_lock::is_supported() { 8 | println!("Session lock not supported") 9 | } 10 | 11 | let app = gtk::Application::new( 12 | Some("com.github.wmww.gtk4-layer-shell.session-lock-example"), 13 | Default::default(), 14 | ); 15 | 16 | app.connect_activate(activate); 17 | app.run(); 18 | } 19 | 20 | fn activate(app: >k::Application) { 21 | let lock = SessionLockInstance::new(); 22 | lock.connect_locked(locked); 23 | lock.connect_failed(failed); 24 | 25 | lock.connect_unlocked(clone!( 26 | #[weak] 27 | app, 28 | move |_| unlocked(&app) 29 | )); 30 | 31 | if !lock.lock() { 32 | // Error message already shown when handling the ::failed signal 33 | return; 34 | } 35 | 36 | let display = gdk::Display::default().unwrap(); 37 | let monitors = display.monitors(); 38 | for monitor in monitors.iter::() { 39 | let monitor = monitor.unwrap().downcast::().unwrap(); 40 | let window = gtk::ApplicationWindow::new(app); 41 | lock.assign_window_to_monitor(&window, &monitor); 42 | 43 | let bbox = gtk::Box::builder() 44 | .orientation(gtk::Orientation::Vertical) 45 | .halign(gtk::Align::Center) 46 | .valign(gtk::Align::Center) 47 | .spacing(10) 48 | .build(); 49 | 50 | let label = gtk::Label::new(Some("GTK4 Session Lock Example")); 51 | let button = gtk::Button::builder() 52 | .label("Unlock") 53 | .halign(gtk::Align::Center) 54 | .valign(gtk::Align::Center) 55 | .build(); 56 | 57 | let lock = lock.clone(); 58 | button.connect_clicked(move |_| lock.unlock()); 59 | 60 | // Not displayed, but allows testing that creating popups doesn't crash GTK 61 | button.set_tooltip_text(Some("Foo Bar Baz")); 62 | 63 | bbox.append(&label); 64 | bbox.append(&button); 65 | window.set_child(Some(&bbox)); 66 | window.present(); 67 | } 68 | } 69 | 70 | fn locked(_: &SessionLockInstance) { 71 | println!("Session locked successfully"); 72 | } 73 | 74 | fn failed(_: &SessionLockInstance) { 75 | eprintln!("The session could not be locked"); 76 | } 77 | 78 | fn unlocked(app: >k::Application) { 79 | println!("Session unlocked"); 80 | app.quit(); 81 | } 82 | -------------------------------------------------------------------------------- /gtk4-session-lock/src/auto/instance.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | use crate::{ffi}; 7 | use glib::{object::ObjectType as _,prelude::*,signal::{connect_raw, SignalHandlerId},translate::*}; 8 | use std::{boxed::Box as Box_}; 9 | 10 | glib::wrapper! { 11 | /// An instance of the object used to control locking the screen. 12 | /// Multiple instances can exist at once, but only one can be locked at a time. 13 | /// 14 | /// ## Signals 15 | /// 16 | /// 17 | /// #### `failed` 18 | /// The ::failed signal is fired when the lock could not be acquired. 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// #### `locked` 24 | /// The ::locked signal is fired when the screen is successfully locked. 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// #### `unlocked` 30 | /// The ::unlocked signal is fired when the session is unlocked, which may have been caused by a call to 31 | /// [`Instance::unlock()`][crate::Instance::unlock()] or by the compositor. 32 | /// 33 | /// 34 | #[doc(alias = "GtkSessionLockInstance")] 35 | pub struct Instance(Object); 36 | 37 | match fn { 38 | type_ => || ffi::gtk_session_lock_instance_get_type(), 39 | } 40 | } 41 | 42 | impl Instance { 43 | /// 44 | /// # Returns 45 | /// 46 | /// new session lock instance 47 | #[doc(alias = "gtk_session_lock_instance_new")] 48 | pub fn new() -> Instance { 49 | assert_initialized_main_thread!(); 50 | unsafe { 51 | from_glib_full(ffi::gtk_session_lock_instance_new()) 52 | } 53 | } 54 | 55 | /// This must be called with a different window once for each monitor, immediately after calling 56 | /// `gtk_session_lock_lock()`. Hiding a window that is active on a monitor or not letting a window be resized by the 57 | /// library is not allowed (may result in a Wayland protocol error). 58 | /// ## `window` 59 | /// The GTK Window to use as a lock surface 60 | /// ## `monitor` 61 | /// The monitor to show it on 62 | #[doc(alias = "gtk_session_lock_instance_assign_window_to_monitor")] 63 | pub fn assign_window_to_monitor(&self, window: &impl IsA, monitor: &gdk::Monitor) { 64 | unsafe { 65 | ffi::gtk_session_lock_instance_assign_window_to_monitor(self.to_glib_none().0, window.as_ref().to_glib_none().0, monitor.to_glib_none().0); 66 | } 67 | } 68 | 69 | /// Returns if this instance currently holds a lock. 70 | #[doc(alias = "gtk_session_lock_instance_is_locked")] 71 | pub fn is_locked(&self) -> bool { 72 | unsafe { 73 | from_glib(ffi::gtk_session_lock_instance_is_locked(self.to_glib_none().0)) 74 | } 75 | } 76 | 77 | /// Lock the screen. This should be called before assigning any windows to monitors. If this function fails the ::failed 78 | /// signal is emitted, if it succeeds the ::locked signal is emitted. The ::failed signal may be emitted before the 79 | /// function returns (for example, if another [`Instance`][crate::Instance] holds a lock) or later (if another process holds a 80 | /// lock). The only case where neither signal is triggered is if the instance is already locked. 81 | /// 82 | /// # Returns 83 | /// 84 | /// false on immediate fail, true if lock acquisition was successfully started 85 | #[doc(alias = "gtk_session_lock_instance_lock")] 86 | pub fn lock(&self) -> bool { 87 | unsafe { 88 | from_glib(ffi::gtk_session_lock_instance_lock(self.to_glib_none().0)) 89 | } 90 | } 91 | 92 | /// If the screen is locked by this instance unlocks it and fires ::unlocked. Otherwise has no effect 93 | #[doc(alias = "gtk_session_lock_instance_unlock")] 94 | pub fn unlock(&self) { 95 | unsafe { 96 | ffi::gtk_session_lock_instance_unlock(self.to_glib_none().0); 97 | } 98 | } 99 | 100 | /// The ::failed signal is fired when the lock could not be acquired. 101 | #[doc(alias = "failed")] 102 | pub fn connect_failed(&self, f: F) -> SignalHandlerId { 103 | unsafe extern "C" fn failed_trampoline(this: *mut ffi::GtkSessionLockInstance, f: glib::ffi::gpointer) { 104 | let f: &F = &*(f as *const F); 105 | f(&from_glib_borrow(this)) 106 | } 107 | unsafe { 108 | let f: Box_ = Box_::new(f); 109 | connect_raw(self.as_ptr() as *mut _, c"failed".as_ptr() as *const _, 110 | Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(failed_trampoline:: as *const ())), Box_::into_raw(f)) 111 | } 112 | } 113 | 114 | /// The ::locked signal is fired when the screen is successfully locked. 115 | #[doc(alias = "locked")] 116 | pub fn connect_locked(&self, f: F) -> SignalHandlerId { 117 | unsafe extern "C" fn locked_trampoline(this: *mut ffi::GtkSessionLockInstance, f: glib::ffi::gpointer) { 118 | let f: &F = &*(f as *const F); 119 | f(&from_glib_borrow(this)) 120 | } 121 | unsafe { 122 | let f: Box_ = Box_::new(f); 123 | connect_raw(self.as_ptr() as *mut _, c"locked".as_ptr() as *const _, 124 | Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(locked_trampoline:: as *const ())), Box_::into_raw(f)) 125 | } 126 | } 127 | 128 | /// The ::unlocked signal is fired when the session is unlocked, which may have been caused by a call to 129 | /// [`unlock()`][Self::unlock()] or by the compositor. 130 | #[doc(alias = "unlocked")] 131 | pub fn connect_unlocked(&self, f: F) -> SignalHandlerId { 132 | unsafe extern "C" fn unlocked_trampoline(this: *mut ffi::GtkSessionLockInstance, f: glib::ffi::gpointer) { 133 | let f: &F = &*(f as *const F); 134 | f(&from_glib_borrow(this)) 135 | } 136 | unsafe { 137 | let f: Box_ = Box_::new(f); 138 | connect_raw(self.as_ptr() as *mut _, c"unlocked".as_ptr() as *const _, 139 | Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(unlocked_trampoline:: as *const ())), Box_::into_raw(f)) 140 | } 141 | } 142 | } 143 | 144 | impl Default for Instance { 145 | fn default() -> Self { 146 | Self::new() 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /gtk4-session-lock/src/auto/mod.rs: -------------------------------------------------------------------------------- 1 | // This file was generated by gir (https://github.com/gtk-rs/gir) 2 | // from .. 3 | // from ../gir-files 4 | // DO NOT EDIT 5 | 6 | mod instance; 7 | pub use self::instance::Instance; 8 | 9 | -------------------------------------------------------------------------------- /gtk4-session-lock/src/auto/versions.txt: -------------------------------------------------------------------------------- 1 | Generated by gir (https://github.com/gtk-rs/gir @ 3491754499c3) 2 | from .. (@ c61a074aeeb8) 3 | from ../gir-files (@ 07aa7d177618) 4 | -------------------------------------------------------------------------------- /gtk4-session-lock/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | #![deny(warnings)] 3 | 4 | use gtk4_session_lock_sys as ffi; 5 | 6 | macro_rules! assert_initialized_main_thread { 7 | () => { 8 | if !::gtk::is_initialized_main_thread() { 9 | if ::gtk::is_initialized() { 10 | panic!("GTK may only be used from the main thread."); 11 | } else { 12 | panic!("GTK has not been initialized. Call `gtk::init` first."); 13 | } 14 | } 15 | }; 16 | } 17 | 18 | mod auto; 19 | pub use auto::Instance; 20 | 21 | mod manual; 22 | pub use manual::is_supported; 23 | -------------------------------------------------------------------------------- /gtk4-session-lock/src/manual.rs: -------------------------------------------------------------------------------- 1 | use glib::translate::from_glib; 2 | 3 | use crate::ffi; 4 | 5 | /// May block for a Wayland roundtrip the first time it's called. 6 | /// 7 | /// # Returns 8 | /// 9 | /// [`true`] if the platform is Wayland and Wayland compositor supports the 10 | /// Session Lock protocol. 11 | #[doc(alias = "gtk_session_lock_is_supported")] 12 | pub fn is_supported() -> bool { 13 | unsafe { from_glib(ffi::gtk_session_lock_is_supported()) } 14 | } 15 | --------------------------------------------------------------------------------