mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf4e8cd233 | |||
| 34b05e8671 | |||
| d4e1b2231b | |||
| e81f356908 | |||
| 1013147ba3 | |||
| 38f388565f | |||
| f9ea905987 | |||
| 7a8355bb4d | |||
| 781edba45f | |||
| 6d62c26a0f | |||
| 5ea9092a49 | |||
| 43a2648e57 | |||
| ba129e98e3 | |||
| b9b4f31b6f | |||
| 5f3528a2ee | |||
| a5777b9473 | |||
| 6dc4f6a622 | |||
| 85d42db05d | |||
| d6db202a03 | |||
| 3a85e77518 | |||
| e0bf056ba3 | |||
| 39bdfece9a | |||
| 26a6e0cd98 | |||
| b12a56e4e6 | |||
| f6e1b3133e | |||
| dc93f1c1fd | |||
| a6febb86aa | |||
| 3b76cb7b3d | |||
| 271534e115 | |||
| af30cc8df6 | |||
| a003e01307 | |||
| 38df8d4f33 | |||
| 6d0505e684 | |||
| 8b4517a87b | |||
| c7a8c44a0d | |||
| 25c1c04349 | |||
| 36af02ad34 | |||
| a9f0f4d44f | |||
| e3101ced70 | |||
| ea438b21e9 | |||
| 42bd107795 | |||
| a2767041d9 | |||
| 5bda3041d0 | |||
| 54076b7632 | |||
| c9873a0885 | |||
| 7a6be2a923 | |||
| bba9ca1fa2 | |||
| af9ce53310 | |||
| 0044578681 | |||
| 0c09f2529b | |||
| 2fb993d221 | |||
| 67361f88fd | |||
| f74d83dcca | |||
| 055a94de3d | |||
| 52c579d556 | |||
| 5edd91d37b | |||
| 378a90e4b0 | |||
| 871cf4ba9a | |||
| f49ecc31c4 | |||
| 15b4acc17e | |||
| 43577f4d97 | |||
| 8fba696d8e | |||
| 2e3935d77d | |||
| 53b7c08363 | |||
| 76c3bb20ba | |||
| 91b6a111cf | |||
| 98a42c5557 | |||
| e19e1f0f10 | |||
| c0ddf3f9ff | |||
| 4ac4cb4a44 | |||
| 365274e5e2 | |||
| 672bf3e1ff | |||
| fefc0bc0a7 | |||
| 0b1a6c76ec | |||
| 485e667fec | |||
| 8f442dee06 | |||
| 9c09bc730f | |||
| 7b065f8618 | |||
| 60fbcd2329 | |||
| 5ac440a760 | |||
| 0e3d078a85 | |||
| 36efd6e3f9 | |||
| 30a9c6c31b | |||
| bc0a06226a | |||
| ed799f5afc | |||
| 007d35541d | |||
| e46a27351d | |||
| 56901eed5d | |||
| 48fe08caf4 | |||
| df00f0328e | |||
| d85eaf9799 | |||
| 25cbb739ae | |||
| 88339633b1 | |||
| 22e43193e0 | |||
| 7a2379ad35 | |||
| fe2c2eec29 | |||
| 746a7e81b7 | |||
| 51b6a495c5 | |||
| bb40a35ccf | |||
| 37c6412e80 | |||
| 19c8fca836 | |||
| 186e0b608a | |||
| ce501bca9e | |||
| 45e9bb769d | |||
| dfb3683187 | |||
| ce9ba00d54 | |||
| 37458d94b2 | |||
| 044f14f8f9 | |||
| 4c02f3bba4 | |||
| b55a80c641 | |||
| e0b0b04b44 | |||
| ed14e8da84 | |||
| e53f8527b0 | |||
| da3dc913a6 | |||
| f3f6e79eec | |||
| 83ec369536 | |||
| 97dfd2b1a0 | |||
| 730eab09fb | |||
| a23ce10311 | |||
| 2f18d8e328 | |||
| 7aec37f5c9 | |||
| 07080a0431 | |||
| aa47223c19 | |||
| 10a6d6ae45 | |||
| 7db864d203 | |||
| 8d7b22d1a8 | |||
| 0407ac5e4c | |||
| a18d24fc24 | |||
| 2bacc80c93 | |||
| c91638c12e | |||
| f8a0c9df2c | |||
| 6bab912383 | |||
| 3edb8fd906 | |||
| c9b1514d63 | |||
| 2066737024 | |||
| f918eabe6a | |||
| 0698f167e5 | |||
| 242ebf2945 | |||
| 9858599ac1 | |||
| abac28a65c | |||
| a7186a0441 | |||
| 1911cf3f55 | |||
| 09da884cd8 |
+140
-54
@@ -8,23 +8,17 @@ on:
|
||||
- cron: '0 0 1 * *' # Monthly
|
||||
|
||||
env:
|
||||
RUN_SLOW_TESTS: 1
|
||||
DEPS_APT: curl gcc clang libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libdbus-1-dev libsystemd-dev libseat-dev libpipewire-0.3-dev libpango1.0-dev libdisplay-info-dev
|
||||
DEPS_DNF: cargo gcc clang libudev-devel libgbm-devel libxkbcommon-devel wayland-devel libinput-devel dbus-devel systemd-devel libseat-devel pipewire-devel pango-devel cairo-gobject-devel libdisplay-info-devel
|
||||
DEPS_APK: cargo clang-libclang eudev-dev glib-dev libdisplay-info-dev libinput-dev libseat-dev libxkbcommon-dev mesa-dev pango-dev pipewire-dev tar
|
||||
DEPS_PKG: git curl rust llvm pkgconf pixman libudev-devd libdisplay-info seatd libinput libxkbcommon pipewire mesa-libs cairo devel/glib20 gettext-runtime harfbuzz pango
|
||||
|
||||
jobs:
|
||||
build:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
configuration: [debug, release]
|
||||
|
||||
include:
|
||||
- configuration: release
|
||||
release-flag: '--release'
|
||||
|
||||
name: test - ${{ matrix.configuration }}
|
||||
name: test
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
@@ -40,46 +34,87 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: ${{ matrix.configuration }}
|
||||
|
||||
- name: Check (no default features)
|
||||
run: cargo check ${{ matrix.release-flag }} --no-default-features
|
||||
|
||||
- name: Check (just dbus)
|
||||
run: cargo check ${{ matrix.release-flag }} --no-default-features --features dbus
|
||||
|
||||
- name: Check (just systemd)
|
||||
run: cargo check ${{ matrix.release-flag }} --no-default-features --features systemd
|
||||
|
||||
- name: Check (just dinit)
|
||||
run: cargo check ${{ matrix.release-flag }} --no-default-features --features dinit
|
||||
|
||||
- name: Check (just xdp-gnome-screencast)
|
||||
run: cargo check ${{ matrix.release-flag }} --no-default-features --features xdp-gnome-screencast
|
||||
|
||||
- name: Check
|
||||
run: cargo check ${{ matrix.release-flag }}
|
||||
|
||||
- name: Build (with profiling)
|
||||
run: cargo build ${{ matrix.release-flag }} --features profile-with-tracy
|
||||
|
||||
- name: Build tests
|
||||
run: cargo test --no-run --all --exclude niri-visual-tests ${{ matrix.release-flag }}
|
||||
run: cargo test --no-run --all --exclude niri-visual-tests
|
||||
|
||||
- name: Test
|
||||
run: cargo test --all --exclude niri-visual-tests ${{ matrix.release-flag }} -- --nocapture
|
||||
run: cargo test --all --exclude niri-visual-tests -- --nocapture
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
name: check feature combinations
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y ${{ env.DEPS_APT }}
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Check (no default features)
|
||||
run: cargo check --no-default-features
|
||||
|
||||
- name: Check (just dbus)
|
||||
run: cargo check --no-default-features --features dbus
|
||||
|
||||
- name: Check (just systemd)
|
||||
run: cargo check --no-default-features --features systemd
|
||||
|
||||
- name: Check (just dinit)
|
||||
run: cargo check --no-default-features --features dinit
|
||||
|
||||
- name: Check (just xdp-gnome-screencast)
|
||||
run: cargo check --no-default-features --features xdp-gnome-screencast
|
||||
|
||||
- name: Check
|
||||
run: cargo check
|
||||
|
||||
- name: Build (with profiling)
|
||||
run: cargo build --features profile-with-tracy
|
||||
|
||||
build-musl:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
name: alpine musl
|
||||
runs-on: ubuntu-24.04
|
||||
container: alpine:3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Install Deps
|
||||
run: apk add --no-cache ${{ env.DEPS_APK }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build
|
||||
run: cargo build --no-default-features --features dbus,xdp-gnome-screencast
|
||||
|
||||
# Job that runs randomized tests for a longer period of time.
|
||||
# Also runs normal slow tests.
|
||||
randomized-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
name: randomized tests
|
||||
name: randomized and slow tests
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
RUN_SLOW_TESTS: 1
|
||||
PROPTEST_CASES: 200000
|
||||
PROPTEST_MAX_LOCAL_REJECTS: 200000
|
||||
PROPTEST_MAX_GLOBAL_REJECTS: 200000
|
||||
@@ -210,6 +245,47 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo build --all
|
||||
|
||||
freebsd:
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
CARGO_HOME: /home/runner/work/niri/niri/cargo-home
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
# Required for the rust-cache action to work.
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
# FIXME: doesn't seem to cache the builds, only the downloads for some unknown reason.
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-all-crates: true
|
||||
|
||||
# Remove man-db triggers to speed up Ubuntu upgrade by a minute or two during vmactions/freebsd-vm action run.
|
||||
- run: |
|
||||
sudo rm /var/lib/dpkg/info/man-db.*
|
||||
|
||||
- name: Build
|
||||
uses: vmactions/freebsd-vm@966989c456d41351f095a421f60e71342d3bce41 # v1.2.1
|
||||
with:
|
||||
prepare: |
|
||||
pkg update -f
|
||||
pkg install -y ${{ env.DEPS_PKG }}
|
||||
run: |
|
||||
curl -o patch-pipewire_init 'https://cgit.freebsd.org/ports/plain/x11-wm/niri/files/patch-pipewire_init?id=f3f7e555b06d9a87d63c047ce3e82e936a11f2fe'
|
||||
|
||||
export CARGO_HOME="$PWD/cargo-home"
|
||||
|
||||
cargo fetch
|
||||
|
||||
( cd $CARGO_HOME/git/checkouts/pipewire-rs-*/*/; patch -p2 < $CARGO_HOME/../patch-pipewire_init; )
|
||||
|
||||
cargo build \
|
||||
--offline \
|
||||
--no-default-features --features dbus,xdp-gnome-screencast
|
||||
|
||||
nix:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
@@ -228,21 +304,10 @@ jobs:
|
||||
- run: nix flake check
|
||||
continue-on-error: true
|
||||
|
||||
check-links:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: lycheeverse/lychee-action@v2.0.2 # later versions break fragment checks. don't bump until this is fixed: https://github.com/lycheeverse/lychee/issues/1574
|
||||
with:
|
||||
args: --offline --include-fragments 'wiki/*.md'
|
||||
|
||||
publish-wiki:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
needs:
|
||||
- build
|
||||
- check-links
|
||||
- publish-docs
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -251,29 +316,50 @@ jobs:
|
||||
with:
|
||||
lfs: true
|
||||
show-progress: false
|
||||
- uses: Andrew-Chen-Wang/github-wiki-action@b7e552d7cb0fa7f83e459012ffc6840fd87bcb83
|
||||
|
||||
rustdoc:
|
||||
needs: build
|
||||
- uses: Andrew-Chen-Wang/github-wiki-action@b7e552d7cb0fa7f83e459012ffc6840fd87bcb83
|
||||
with:
|
||||
path: docs/wiki/
|
||||
|
||||
publish-docs:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
needs:
|
||||
- test
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
show-progress: false
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --locked --all-extras --dev
|
||||
working-directory: docs/
|
||||
|
||||
- name: Generate niri documentation
|
||||
run: uv run mkdocs build
|
||||
working-directory: docs/
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Generate documentation
|
||||
- name: Generate rustdoc documentation
|
||||
run: cargo doc --no-deps -p niri-ipc
|
||||
|
||||
- run: cp ./resources/rustdoc-index.html ./target/doc/index.html
|
||||
- run: mkdir -p publish/niri_ipc
|
||||
- run: cp -r ./target/doc/* ./publish/
|
||||
- run: cp -r ./docs/site/* ./publish/
|
||||
|
||||
- name: Deploy documentation
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc
|
||||
publish_dir: ./publish
|
||||
force_orphan: true
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# Contributing to niri
|
||||
|
||||
Thanks for your interest in niri!
|
||||
The project has grown quite a bit, and we could use all help that we can.
|
||||
|
||||
Make sure to join our Matrix chat if you have any questions or want to discuss anything: https://matrix.to/#/#niri:matrix.org
|
||||
|
||||
## Issues and discussions
|
||||
|
||||
This is a good way to help many new and existing users without programming knowledge.
|
||||
|
||||
- Answer and help people in GitHub issues and discussions.
|
||||
- Check and point out duplicate issues.
|
||||
- Check for issues that are likely application bugs (and not niri bugs).
|
||||
- Ask or try to reproduce on another non-Smithay-based compositor (sway, KDE/KWin, GNOME/Mutter). If the issue reproduces, it's likely an application bug.
|
||||
- Ask or try to reproduce on another *Smithay-based* compositor ([cosmic-comp], [anvil]). If the issue reproduces only on Smithay compositors, it may be a Smithay bug.
|
||||
- Make sure you're testing the Wayland version of the app on all compositors. Apps may silently use X11 when an X11 `$DISPLAY` is available.
|
||||
- Problems with X11 apps should be reported to [xwayland-satellite]. When testing xwayland-satellite on different compositors, make sure you use xwayland-satellite's `$DISPLAY` (rather than another compositor's built-in Xwayland `$DISPLAY`).
|
||||
- After testing, mention where you could and couldn't reproduce, as well as the exact steps to reproduce if the issue is missing them.
|
||||
- Try to reproduce the issue on your own system and write if you could or couldn't reproduce it.
|
||||
- Upvote issues with a thumbs up reaction as you like.
|
||||
- Ideas and feature requests from new users should go to Discussions.
|
||||
|
||||
If your issue is a duplicate, or not a niri issue (application bug, hardware problem, configuration problem), then please close it.
|
||||
|
||||
## Reviewing and testing pull requests
|
||||
|
||||
With the growing popularity, the volume of pull requests is honestly more than I can manage myself in my free time.
|
||||
I would really appreciate help with testing and reviewing them.
|
||||
|
||||
### Testing
|
||||
|
||||
Pick a pull request you like, then build it and give it a go.
|
||||
The [Developing niri wiki page](https://yalter.github.io/niri/Development:-Developing-niri) has guidance on running niri test builds.
|
||||
|
||||
Be really thorough with your testing.
|
||||
We're striving for polished features in niri, so point out any issues and bugs, even small ones like animation jank.
|
||||
|
||||
- Think of weird edge cases or unexpected interactions and try them to see that they work reasonably.
|
||||
- Try to break the feature and check that it behaves well.
|
||||
- Where applicable, try different input devices: keyboard, mouse, trackpad, tablet, touchscreen.
|
||||
- Watch out for any new performance drops.
|
||||
|
||||
For bug fixes, first make sure you can reproduce the bug, then do the same steps in the PR test build, and verify that the bug is fixed.
|
||||
Be similarly thorough: test any similar or related edge cases to verify that the fix doesn't introduce any new problems.
|
||||
|
||||
Write your findings in the pull request: any issues you found, or if everything worked well.
|
||||
Re-test after the author updates the code to see that your issues were fixed.
|
||||
|
||||
Don't hesitate to test even if someone else already did; very frequently different people will stumble upon different problems.
|
||||
|
||||
### Reviewing
|
||||
|
||||
Reviewing pull requests is something I need the most help with since there are a lot of them, and it's quite time-consuming.
|
||||
Anyone with code accepted into niri is welcome, but this is not a requirement; even if you aren't familiar with Rust you may find some logic problems.
|
||||
|
||||
Pick a pull request, then review its code.
|
||||
|
||||
- Check that everything looks good, check various conditions for edge cases.
|
||||
- See if there are any scenarios the author forgot to handle.
|
||||
- Check that the code fits well into the rest of niri, follows its design and code style.
|
||||
- I understand this is vague. The idea is: look at the surrounding code and at similar modules (e.g. when implementing a new protocol, check other protocol implementations), and try to follow the style and structure.
|
||||
- Check for unrelated changes that may be better split into their own pull request.
|
||||
- Check that the wiki had been updated if necessary (for example, new config options were documented with examples, and have a correct Since annotation).
|
||||
|
||||
Point out everything you find as review comments (don't forget to submit the review).
|
||||
Be constructive and respectful; some people may be new to programming and Rust.
|
||||
As the author addresses the comments and issues, check the code again to see that the problems were fixed.
|
||||
If everything looks good, say that, so I know someone has reviewed the PR.
|
||||
|
||||
As with testing, don't hesitate to look through and comment even if someone else already had.
|
||||
Extra pairs of eyes catch more problems.
|
||||
|
||||
## Writing pull requests
|
||||
|
||||
When creating pull requests, please keep the following in mind.
|
||||
|
||||
- Make sure new features align with niri's design directions. Ideally, there should be an existing issue or discussion where we settled on that solution.
|
||||
- Keep pull requests focused on a single feature or bug fix with no unrelated changes.
|
||||
- Try to split your changes into small, self-contained commits. Every commit should build and pass tests. This makes it much easier to review your PR, and bisect for regressions in the future.
|
||||
- When addressing PR comments, try to squash the changes straight into the relevant commits.
|
||||
- In some cases when the requested changes are big/unclear, you can leave them as separate commits on top, but please squash and otherwise clean up the history when the changes are finalized.
|
||||
- To update the main branch, please rebase instead of merging. Try to force-push the main update rebase separately from other changes, this way it's easy to skip during review since it's usually not interesting.
|
||||
- When working on bigger features, I usually start with a big messy commit, then gradually split out smaller self-contained changes from it as the code gets into shape.
|
||||
- [git-rebase.io](https://git-rebase.io/) is a helpful guide for splitting commits and cleaning up history in git.
|
||||
- When you address a review comment, mark it as resolved.
|
||||
- Remember to [run tests](https://yalter.github.io/niri/Development:-Developing-niri#tests) and format the code with `cargo +nightly fmt --all`.
|
||||
- For new layout actions, remember to add them to the randomized tests. For weird Wayland handling, adding client-server tests in `src/tests/` could be very useful.
|
||||
- Test your changes by hand thoroughly, including for edge cases and weird interactions. See the Testing section above for some tips.
|
||||
- Remember to document new config options on the wiki.
|
||||
- When opening a pull request, ensure "Allow edits from maintainers" is enabled, so I can make final tweaks before merging.
|
||||
|
||||
|
||||
[cosmic-comp]: https://github.com/pop-os/cosmic-comp
|
||||
[anvil]: https://github.com/Smithay/smithay/tree/master/anvil
|
||||
[xwayland-satellite]: https://github.com/Supreeeme/xwayland-satellite
|
||||
Generated
+640
-574
File diff suppressed because it is too large
Load Diff
+26
-25
@@ -15,15 +15,15 @@ repository = "https://github.com/YaLTeR/niri"
|
||||
rust-version = "1.80.1"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.98"
|
||||
bitflags = "2.9.1"
|
||||
clap = { version = "4.5.38", features = ["derive"] }
|
||||
anyhow = "1.0.99"
|
||||
bitflags = "2.9.2"
|
||||
clap = { version = "4.5.45", features = ["derive"] }
|
||||
insta = "1.43.1"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
serde_json = "1.0.143"
|
||||
tracing = { version = "0.1.41", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tracy-client = { version = "0.18.0", default-features = false }
|
||||
tracy-client = { version = "0.18.2", default-features = false }
|
||||
|
||||
[workspace.dependencies.smithay]
|
||||
# version = "0.4.1"
|
||||
@@ -52,34 +52,35 @@ keywords = ["wayland", "compositor", "tiling", "smithay", "wm"]
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
arrayvec = "0.7.6"
|
||||
async-channel = "2.3.1"
|
||||
async-io = { version = "2.4.0", optional = true }
|
||||
atomic = "0.6.0"
|
||||
async-channel = "2.5.0"
|
||||
async-io = { version = "2.5.0", optional = true }
|
||||
atomic = "0.6.1"
|
||||
bitflags.workspace = true
|
||||
bytemuck = { version = "1.23.0", features = ["derive"] }
|
||||
calloop = { version = "0.14.2", features = ["executor", "futures-io"] }
|
||||
bytemuck = { version = "1.23.2", features = ["derive"] }
|
||||
calloop = { version = "0.14.3", features = ["executor", "futures-io", "signals"] }
|
||||
clap = { workspace = true, features = ["string"] }
|
||||
clap_complete = "4.5.50"
|
||||
clap_complete = "4.5.57"
|
||||
clap_complete_nushell = "4.5.8"
|
||||
directories = "6.0.0"
|
||||
drm-ffi = "0.9.0"
|
||||
fastrand = "2.3.0"
|
||||
futures-util = { version = "0.3.31", default-features = false, features = ["std", "io"] }
|
||||
git-version = "0.3.9"
|
||||
glam = "0.30.3"
|
||||
glam = "0.30.5"
|
||||
input = { version = "0.9.1", features = ["libinput_1_21"] }
|
||||
keyframe = { version = "1.1.1", default-features = false }
|
||||
libc = "0.2.172"
|
||||
libc = "0.2.175"
|
||||
libdisplay-info = "0.2.2"
|
||||
log = { version = "0.4.27", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
niri-config = { version = "25.5.1", path = "niri-config" }
|
||||
niri-ipc = { version = "25.5.1", path = "niri-ipc", features = ["clap"] }
|
||||
ordered-float = "5.0.0"
|
||||
pango = { version = "0.20.10", features = ["v1_44"] }
|
||||
pango = { version = "0.20.12", features = ["v1_44"] }
|
||||
pangocairo = "0.20.10"
|
||||
pipewire = { git = "https://gitlab.freedesktop.org/pipewire/pipewire-rs.git", optional = true, features = ["v0_3_33"] }
|
||||
png = "0.17.16"
|
||||
portable-atomic = { version = "1.11.0", default-features = false, features = ["float"] }
|
||||
profiling = "1.0.16"
|
||||
portable-atomic = { version = "1.11.1", default-features = false, features = ["float"] }
|
||||
profiling = "1.0.17"
|
||||
sd-notify = "0.4.5"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -88,10 +89,10 @@ tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
tracy-client.workspace = true
|
||||
url = { version = "2.5.4", optional = true }
|
||||
wayland-backend = "0.3.10"
|
||||
wayland-scanner = "0.31.6"
|
||||
xcursor = "0.3.8"
|
||||
zbus = { version = "5.7.0", optional = true }
|
||||
wayland-backend = "0.3.11"
|
||||
wayland-scanner = "0.31.7"
|
||||
xcursor = "0.3.10"
|
||||
zbus = { version = "5.9.0", optional = true }
|
||||
|
||||
[dependencies.smithay]
|
||||
workspace = true
|
||||
@@ -115,10 +116,10 @@ features = [
|
||||
approx = "0.5.1"
|
||||
calloop-wayland-source = "0.4.0"
|
||||
insta.workspace = true
|
||||
proptest = "1.6.0"
|
||||
proptest-derive = { version = "0.5.1", features = ["boxed_union"] }
|
||||
rayon = "1.10.0"
|
||||
wayland-client = "0.31.10"
|
||||
proptest = "1.7.0"
|
||||
proptest-derive = { version = "0.6.0", features = ["boxed_union"] }
|
||||
rayon = "1.11.0"
|
||||
wayland-client = "0.31.11"
|
||||
xshell = "0.2.7"
|
||||
|
||||
[features]
|
||||
@@ -152,7 +153,7 @@ insta.opt-level = 3
|
||||
similar.opt-level = 3
|
||||
|
||||
[package.metadata.generate-rpm]
|
||||
version = "25.02"
|
||||
version = "25.05.1"
|
||||
assets = [
|
||||
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
|
||||
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/YaLTeR/niri/wiki/Getting-Started">Getting Started</a> | <a href="https://github.com/YaLTeR/niri/wiki/Configuration:-Overview">Configuration</a> | <a href="https://github.com/YaLTeR/niri/discussions/325">Setup Showcase</a>
|
||||
<a href="https://yalter.github.io/niri/Getting-Started.html">Getting Started</a> | <a href="https://yalter.github.io/niri/Configuration%3A-Introduction.html">Configuration</a> | <a href="https://github.com/YaLTeR/niri/discussions/325">Setup Showcase</a>
|
||||
</p>
|
||||
|
||||

|
||||
@@ -29,16 +29,16 @@ When a monitor disconnects, its workspaces will move to another monitor, but upo
|
||||
## Features
|
||||
|
||||
- Built from the ground up for scrollable tiling
|
||||
- [Dynamic workspaces](https://github.com/YaLTeR/niri/wiki/Workspaces) like in GNOME
|
||||
- [Dynamic workspaces](https://yalter.github.io/niri/Workspaces.html) like in GNOME
|
||||
- An [Overview](https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995) that zooms out workspaces and windows
|
||||
- Built-in screenshot UI
|
||||
- Monitor and window screencasting through xdg-desktop-portal-gnome
|
||||
- You can [block out](https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules#block-out-from) sensitive windows from screencasts
|
||||
- [Dynamic cast target](https://github.com/YaLTeR/niri/wiki/Screencasting#dynamic-screencast-target) that can change what it shows on the go
|
||||
- You can [block out](https://yalter.github.io/niri/Configuration%3A-Window-Rules.html#block-out-from) sensitive windows from screencasts
|
||||
- [Dynamic cast target](https://yalter.github.io/niri/Screencasting.html#dynamic-screencast-target) that can change what it shows on the go
|
||||
- [Touchpad](https://github.com/YaLTeR/niri/assets/1794388/946a910e-9bec-4cd1-a923-4a9421707515) and [mouse](https://github.com/YaLTeR/niri/assets/1794388/8464e65d-4bf2-44fa-8c8e-5883355bd000) gestures
|
||||
- Group windows into [tabs](https://github.com/YaLTeR/niri/wiki/Tabs)
|
||||
- Group windows into [tabs](https://yalter.github.io/niri/Tabs.html)
|
||||
- Configurable layout: gaps, borders, struts, window sizes
|
||||
- [Gradient borders](https://github.com/YaLTeR/niri/wiki/Configuration:-Layout#gradients) with Oklab and Oklch support
|
||||
- [Gradient borders](https://yalter.github.io/niri/Configuration%3A-Layout.html#gradients) with Oklab and Oklch support
|
||||
- [Animations](https://github.com/YaLTeR/niri/assets/1794388/ce178da2-af9e-4c51-876f-8709c241d95e) with support for [custom shaders](https://github.com/YaLTeR/niri/assets/1794388/27a238d6-0a22-4692-b794-30dc7a626fad)
|
||||
- Live-reloading config
|
||||
|
||||
@@ -46,14 +46,17 @@ When a monitor disconnects, its workspaces will move to another monitor, but upo
|
||||
|
||||
https://github.com/YaLTeR/niri/assets/1794388/bce834b0-f205-434e-a027-b373495f9729
|
||||
|
||||
Also check out this video from Brodie Robertson that showcases a lot of the niri functionality: [Niri Is My New Favorite Wayland Compositor](https://youtu.be/DeYx2exm04M)
|
||||
|
||||
## Status
|
||||
|
||||
Niri is stable for day-to-day use and does most things expected of a Wayland compositor.
|
||||
Many people are daily-driving niri, and are happy to help in our [Matrix channel].
|
||||
|
||||
Give it a try!
|
||||
Follow the instructions on the [Getting Started](https://github.com/YaLTeR/niri/wiki/Getting-Started) wiki page.
|
||||
Follow the instructions on the [Getting Started](https://yalter.github.io/niri/Getting-Started.html) page.
|
||||
Have your [waybar]s and [fuzzel]s ready: niri is not a complete desktop environment.
|
||||
Also check out [awesome-niri], a list of niri-related links and projects.
|
||||
|
||||
Here are some points you may have questions about:
|
||||
|
||||
@@ -68,15 +71,36 @@ We have touchpad gestures, but no touchscreen gestures yet.
|
||||
You can check on [wayland.app](https://wayland.app) at the bottom of each protocol's page.
|
||||
- **Performance**: while I run niri on beefy machines, I try to stay conscious of performance.
|
||||
I've seen someone use it fine on an Eee PC 900 from 2008, of all things.
|
||||
- **Xwayland**: no built-in support, but xwayland-satellite is [easy to set up](https://github.com/YaLTeR/niri/wiki/Xwayland#using-xwayland-satellite) and works very well.
|
||||
- **Xwayland**: no built-in support, but xwayland-satellite is [easy to set up](https://yalter.github.io/niri/Xwayland.html#using-xwayland-satellite) and works very well.
|
||||
- Steam and games, including Proton: work perfectly through xwayland-satellite.
|
||||
- JetBrains IDEs, Ghidra: work well through xwayland-satellite.
|
||||
- Discord and other Electron apps: work well through xwayland-satellite.
|
||||
- Chromium and VSCode: work perfectly natively on Wayland with the right flags.
|
||||
- X11 apps that want to position windows or bars at specific screen coordinates: won't work well; you can run them in a nested compositor like [labwc](https://github.com/YaLTeR/niri/wiki/Xwayland#using-the-labwc-wayland-compositor) or [rootful Xwayland](https://github.com/YaLTeR/niri/wiki/Xwayland#directly-running-xwayland-in-rootful-mode).
|
||||
- X11 apps that want to position windows or bars at specific screen coordinates: won't work well; you can run them in a nested compositor like [labwc](https://yalter.github.io/niri/Xwayland.html#using-the-labwc-wayland-compositor) or [rootful Xwayland](https://yalter.github.io/niri/Xwayland.html#directly-running-xwayland-in-rootful-mode).
|
||||
- Display scaling (integer or fractional) keeps X11 apps crisp, but you need the latest xwayland-satellite.
|
||||
For games, you can run them in [gamescope] at native resolution, even with display scaling.
|
||||
|
||||
## Media
|
||||
|
||||
[niri: Making a Wayland compositor in Rust](https://youtu.be/Kmz8ODolnDg?list=PLRdS-n5seLRqrmWDQY4KDqtRMfIwU0U3T) · *December 2024*
|
||||
|
||||
My talk from the 2024 Moscow RustCon about niri, and how I do randomized property testing and profiling, and measure input latency.
|
||||
The talk is in Russian, but I prepared full English subtitles that you can find in YouTube's subtitle language selector.
|
||||
|
||||
[An interview with Ivan, the developer behind Niri](https://www.trommelspeicher.de/podcast/special_the_developer_behind_niri) · *June 2025*
|
||||
|
||||
An interview by a German tech podcast Das Triumvirat (in English).
|
||||
We talk about niri development and history, and my experience building and maintaining niri.
|
||||
|
||||
[A tour of the niri scrolling-tiling Wayland compositor](https://lwn.net/Articles/1025866/) · *July 2025*
|
||||
|
||||
An LWN article with a nice overview and introduction to niri.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you'd like to help with niri, there are plenty of both coding- and non-coding-related ways to do so.
|
||||
See [CONTRIBUTING.md](https://github.com/YaLTeR/niri/blob/main/CONTRIBUTING.md) for an overview.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Niri is heavily inspired by [PaperWM] which implements scrollable tiling on top of GNOME Shell.
|
||||
@@ -94,20 +118,16 @@ Here are some other projects which implement a similar workflow:
|
||||
- [hyprscrolling] and [hyprslidr]: scrollable tiling on top of Hyprland.
|
||||
- [PaperWM.spoon]: scrollable tiling on top of macOS.
|
||||
|
||||
## Media
|
||||
|
||||
[niri: Making a Wayland compositor in Rust](https://youtu.be/Kmz8ODolnDg?list=PLRdS-n5seLRqrmWDQY4KDqtRMfIwU0U3T)
|
||||
|
||||
My talk from the 2024 Moscow RustCon about niri, and how I do randomized property testing and profiling, and measure input latency.
|
||||
The talk is in Russian, but I prepared full English subtitles that you can find in YouTube's subtitle language selector.
|
||||
|
||||
## Contact
|
||||
|
||||
We have a Matrix chat, feel free to join and ask a question: https://matrix.to/#/#niri:matrix.org
|
||||
Our main communication channel is a Matrix chat, feel free to join and ask a question: https://matrix.to/#/#niri:matrix.org
|
||||
|
||||
We also have a community Discord server: https://discord.gg/vT8Sfjy7sx
|
||||
|
||||
[PaperWM]: https://github.com/paperwm/PaperWM
|
||||
[waybar]: https://github.com/Alexays/Waybar
|
||||
[fuzzel]: https://codeberg.org/dnkl/fuzzel
|
||||
[awesome-niri]: https://github.com/Vortriz/awesome-niri
|
||||
[karousel]: https://github.com/peterfajdiga/karousel
|
||||
[papersway]: https://spwhitton.name/tech/code/papersway/
|
||||
[hyprscrolling]: https://github.com/hyprwm/hyprland-plugins/tree/main/hyprscrolling
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
site
|
||||
__pycache__
|
||||
@@ -0,0 +1,19 @@
|
||||
from __future__ import annotations
|
||||
import re
|
||||
|
||||
# todo: this could be done generically, so that any
|
||||
# ```language,annotation,anything-else
|
||||
# is reduced to
|
||||
# ```language
|
||||
# which is what's supported by mkdocs/pygments
|
||||
# also note: mkdocs provides ways to highlight lines, add line numbers
|
||||
# but these are added as
|
||||
# ```language linenums="1"
|
||||
# and not split by comma
|
||||
def on_page_markdown(
|
||||
markdown: str, *, page, config, files
|
||||
):
|
||||
return re.sub(
|
||||
r",must-fail",
|
||||
'', markdown, flags = re.I | re.M
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
from __future__ import annotations
|
||||
import re
|
||||
from re import Match
|
||||
|
||||
def on_page_markdown(
|
||||
markdown: str, *, page, config, files
|
||||
):
|
||||
def replace(match: Match):
|
||||
matches = match.groups()
|
||||
preposition, version = matches[0], matches[1]
|
||||
return _badge_for_version(preposition, version)
|
||||
|
||||
return re.sub(
|
||||
r"<sup>(Until|Since): (.*?)</sup>",
|
||||
replace, markdown, flags = re.I | re.M
|
||||
)
|
||||
|
||||
def _badge_for_version(preposition: str, version: str):
|
||||
if version == "next release":
|
||||
# we might fail to make real links to release notes on other cases too, but for now this is the one i've found
|
||||
return f"<span class=\"badge\">{preposition}: {version}</span>"
|
||||
else:
|
||||
path = f"https://github.com/YaLTeR/niri/releases/tag/v{version}"
|
||||
return f"<span class=\"badge\">[{preposition}: {version}]({path})</span>"
|
||||
@@ -0,0 +1,109 @@
|
||||
site_name: niri
|
||||
docs_dir: wiki
|
||||
site_url: https://yalter.github.io/niri
|
||||
repo_url: https://github.com/YaLTeR/niri
|
||||
edit_uri: edit/main/docs/wiki/
|
||||
use_directory_urls: false
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: _assets/icons/logo.svg
|
||||
favicon: _assets/icons/logo.svg
|
||||
features:
|
||||
- navigation.instant
|
||||
- search.suggest
|
||||
- content.code.copy
|
||||
- content.action.edit
|
||||
palette:
|
||||
- media: "(prefers-color-scheme)"
|
||||
primary: custom
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
primary: custom
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
primary: custom
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
markdown_extensions:
|
||||
- github-callouts
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.magiclink
|
||||
- pymdownx.snippets
|
||||
- pymdownx.superfences
|
||||
- pymdownx.keys
|
||||
- toc:
|
||||
permalink: '#'
|
||||
plugins:
|
||||
- search
|
||||
hooks:
|
||||
- hooks/shortcodes.py
|
||||
- hooks/remove-must-fail.py
|
||||
extra_css:
|
||||
- _assets/stylesheets/niri.css
|
||||
strict: true
|
||||
validation:
|
||||
nav:
|
||||
omitted_files: warn
|
||||
not_found: warn
|
||||
absolute_links: relative_to_docs
|
||||
links:
|
||||
not_found: warn
|
||||
anchors: warn
|
||||
absolute_links: relative_to_docs
|
||||
unrecognized_links: warn
|
||||
not_in_nav: |
|
||||
_Sidebar.md
|
||||
Configuration:-Overview.md
|
||||
README.md
|
||||
# ah, wouldn't it be nice if we could autogenerate this with wiki/_Sidebar.md
|
||||
nav:
|
||||
- Usage:
|
||||
- Getting Started: Getting-Started.md
|
||||
- Example systemd Setup: Example-systemd-Setup.md
|
||||
- Important Software: Important-Software.md
|
||||
- Workspaces: Workspaces.md
|
||||
- Floating Windows: Floating-Windows.md
|
||||
- Tabs: Tabs.md
|
||||
- Overview: Overview.md
|
||||
- Screencasting: Screencasting.md
|
||||
- Layer‐Shell Components: Layer‐Shell-Components.md
|
||||
- IPC, niri msg: IPC.md
|
||||
- Application-Specific Issues: Application-Issues.md
|
||||
- Nvidia: Nvidia.md
|
||||
- Xwayland: Xwayland.md
|
||||
- Gestures: Gestures.md
|
||||
- Packaging niri: Packaging-niri.md
|
||||
- FAQ: FAQ.md
|
||||
- Configuration:
|
||||
- Introduction: Configuration:-Introduction.md
|
||||
- Input: Configuration:-Input.md
|
||||
- Outputs: Configuration:-Outputs.md
|
||||
- Key Bindings: Configuration:-Key-Bindings.md
|
||||
- Switch Events: Configuration:-Switch-Events.md
|
||||
- Layout: Configuration:-Layout.md
|
||||
- Named Workspaces: Configuration:-Named-Workspaces.md
|
||||
- Miscellaneous: Configuration:-Miscellaneous.md
|
||||
- Window Rules: Configuration:-Window-Rules.md
|
||||
- Layer Rules: Configuration:-Layer-Rules.md
|
||||
- Animations: Configuration:-Animations.md
|
||||
- Gestures: Configuration:-Gestures.md
|
||||
- Debug Options: Configuration:-Debug-Options.md
|
||||
- Development:
|
||||
- Design Principles: Development:-Design-Principles.md
|
||||
- Developing niri: Development:-Developing-niri.md
|
||||
- Documenting niri: Development:-Documenting-niri.md
|
||||
- Fractional Layout: Development:-Fractional-Layout.md
|
||||
- Redraw Loop: Development:-Redraw-Loop.md
|
||||
- Animation Timing: Development:-Animation-Timing.md
|
||||
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "niri-docs"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"markdown-callouts>=0.4.0",
|
||||
"mkdocs-material>=9.6.15",
|
||||
"pygments",
|
||||
]
|
||||
|
||||
# for KDL highlighting support
|
||||
# TODO: use the official pygments package once https://github.com/pygments/pygments/pull/2936 is merged
|
||||
[tool.uv.sources]
|
||||
pygments = { git = "https://github.com/chinatsu/pygments", rev = "0f0b0d4da2839e1285881389155bb4605a0a6dc4" }
|
||||
Generated
+511
@@ -0,0 +1,511 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
|
||||
[[package]]
|
||||
name = "babel"
|
||||
version = "2.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backrefs"
|
||||
version = "5.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59", size = 5765857, upload-time = "2025-06-22T19:34:13.97Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f", size = 380267, upload-time = "2025-06-22T19:34:05.252Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf", size = 392072, upload-time = "2025-06-22T19:34:06.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa", size = 397947, upload-time = "2025-06-22T19:34:08.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b", size = 399843, upload-time = "2025-06-22T19:34:09.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9", size = 411762, upload-time = "2025-06-22T19:34:11.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265, upload-time = "2025-06-22T19:34:12.405Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.7.14"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghp-import"
|
||||
version = "2.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.8.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-callouts"
|
||||
version = "0.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/87/73/ae5aa379f6f7fea9d0bf4cba888f9a31d451d90f80033ae60ae3045770d5/markdown_callouts-0.4.0.tar.gz", hash = "sha256:7ed2c90486967058a73a547781121983839522d67041ae52c4979616f1b2b746", size = 9768, upload-time = "2024-01-22T23:18:18.513Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/b5/7b0a0a52c82bfccd830af2a8cc8add1c5bc932e0204922434954a631dd51/markdown_callouts-0.4.0-py3-none-any.whl", hash = "sha256:ed0da38f29158d93116a0d0c6ecaf9df90b37e0d989b5337d678ee6e6d6550b7", size = 7108, upload-time = "2024-01-22T23:18:17.465Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mergedeep"
|
||||
version = "1.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs"
|
||||
version = "1.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "ghp-import" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "markupsafe" },
|
||||
{ name = "mergedeep" },
|
||||
{ name = "mkdocs-get-deps" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "pyyaml-env-tag" },
|
||||
{ name = "watchdog" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-get-deps"
|
||||
version = "0.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mergedeep" },
|
||||
{ name = "platformdirs" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.6.15"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "babel" },
|
||||
{ name = "backrefs" },
|
||||
{ name = "colorama" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-material-extensions" },
|
||||
{ name = "paginate" },
|
||||
{ name = "pygments" },
|
||||
{ name = "pymdown-extensions" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/c1/f804ba2db2ddc2183e900befe7dad64339a34fa935034e1ab405289d0a97/mkdocs_material-9.6.15.tar.gz", hash = "sha256:64adf8fa8dba1a17905b6aee1894a5aafd966d4aeb44a11088519b0f5ca4f1b5", size = 3951836, upload-time = "2025-07-01T10:14:15.671Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/30/dda19f0495a9096b64b6b3c07c4bfcff1c76ee0fc521086d53593f18b4c0/mkdocs_material-9.6.15-py3-none-any.whl", hash = "sha256:ac969c94d4fe5eb7c924b6d2f43d7db41159ea91553d18a9afc4780c34f2717a", size = 8716840, upload-time = "2025-07-01T10:14:13.18Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material-extensions"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "niri-docs"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "markdown-callouts" },
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "markdown-callouts", specifier = ">=0.4.0" },
|
||||
{ name = "mkdocs-material", specifier = ">=9.6.15" },
|
||||
{ name = "pygments", git = "https://github.com/chinatsu/pygments?rev=0f0b0d4da2839e1285881389155bb4605a0a6dc4" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paginate"
|
||||
version = "0.5.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
source = { git = "https://github.com/chinatsu/pygments?rev=0f0b0d4da2839e1285881389155bb4605a0a6dc4#0f0b0d4da2839e1285881389155bb4605a0a6dc4" }
|
||||
|
||||
[[package]]
|
||||
name = "pymdown-extensions"
|
||||
version = "10.16"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/0a/c06b542ac108bfc73200677309cd9188a3a01b127a63f20cadc18d873d88/pymdown_extensions-10.16.tar.gz", hash = "sha256:71dac4fca63fabeffd3eb9038b756161a33ec6e8d230853d3cecf562155ab3de", size = 853197, upload-time = "2025-06-21T17:56:36.974Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/98/d4/10bb14004d3c792811e05e21b5e5dcae805aacb739bd12a0540967b99592/pymdown_extensions-10.16-py3-none-any.whl", hash = "sha256:f5dd064a4db588cb2d95229fc4ee63a1b16cc8b4d0e6145c0899ed8723da1df2", size = 266143, upload-time = "2025-06-21T17:56:35.356Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml-env-tag"
|
||||
version = "1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "6.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
||||
]
|
||||
@@ -52,6 +52,11 @@ To fix this, run them with the `_JAVA_AWT_WM_NONREPARENTING=1` environment varia
|
||||
There's a bug in rofi-wayland that prevents it from accepting keyboard input on niri with errors in the output.
|
||||
It's been fixed in rofi, but [the fix had not been released yet](https://github.com/davatorium/rofi/discussions/2008).
|
||||
|
||||
### Zen Browser
|
||||
|
||||
For some reason, DMABUF screencasts are disabled in the Zen Browser, so screencasting doesn't work out of the box on niri.
|
||||
To fix it, open `about:config` and set `widget.dmabuf.force-enabled` to `true`.
|
||||
|
||||
### Fullscreen games
|
||||
|
||||
Some video games, both Linux-native and on Wine, have various issues when using non-stacking desktop environments.
|
||||
@@ -21,14 +21,16 @@ debug {
|
||||
force-pipewire-invalid-modifier
|
||||
dbus-interfaces-in-non-session-instances
|
||||
wait-for-frame-completion-before-queueing
|
||||
wait-for-frame-completion-in-pipewire
|
||||
emulate-zero-presentation-time
|
||||
disable-resize-throttling
|
||||
disable-transactions
|
||||
keep-laptop-panel-on-when-lid-is-closed
|
||||
disable-monitor-names
|
||||
strict-new-window-focus-policy
|
||||
honor-xdg-activation-with-invalid-serial
|
||||
honor-xdg-activation-with-invalid-serial
|
||||
skip-cursor-only-updates-during-vrr
|
||||
deactivate-unfocused-windows
|
||||
keep-max-bpc-unchanged
|
||||
}
|
||||
|
||||
binds {
|
||||
@@ -153,22 +155,6 @@ debug {
|
||||
}
|
||||
```
|
||||
|
||||
### `wait-for-frame-completion-in-pipewire`
|
||||
|
||||
<sup>Since: 25.05</sup>
|
||||
|
||||
Wait until every screencast frame is done rendering before handing it over to PipeWire.
|
||||
|
||||
Sometimes helps on NVIDIA to prevent glitched frames when screencasting.
|
||||
|
||||
This debug flag will eventually be removed once we handle this properly (via explicit sync in PipeWire).
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
wait-for-frame-completion-in-pipewire
|
||||
}
|
||||
```
|
||||
|
||||
### `emulate-zero-presentation-time`
|
||||
|
||||
Emulate zero (unknown) presentation time returned from DRM.
|
||||
@@ -275,6 +261,55 @@ debug {
|
||||
}
|
||||
```
|
||||
|
||||
### `skip-cursor-only-updates-during-vrr`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Skips redrawing the screen from cursor input while variable refresh rate is active.
|
||||
|
||||
Useful for games where the cursor isn't drawn internally to prevent erratic VRR shifts in response to cursor movement.
|
||||
|
||||
Note that the current implementation has some issues, for example when there's nothing redrawing the screen (like a game), the rendering will appear to completely freeze (since cursor movements won't cause redraws).
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
skip-cursor-only-updates-during-vrr
|
||||
}
|
||||
```
|
||||
|
||||
### `deactivate-unfocused-windows`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Some clients (notably, Chromium- and Electron-based, like Teams or Slack) erroneously use the Activated xdg window state instead of keyboard focus for things like deciding whether to send notifications for new messages, or for picking where to show an IME popup.
|
||||
Niri keeps the Activated state on unfocused workspaces and invisible tabbed windows (to reduce unwanted animations), surfacing bugs in these applications.
|
||||
|
||||
Set this debug flag to work around these problems.
|
||||
It will cause niri to drop the Activated state for all unfocused windows.
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
deactivate-unfocused-windows
|
||||
}
|
||||
```
|
||||
|
||||
### `keep-max-bpc-unchanged`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
When connecting monitors, niri sets their max bpc to 8 in order to reduce display bandwidth and to potentially allow more monitors to be connected at once.
|
||||
Restricting bpc to 8 is not a problem since we don't support HDR or color management yet and can't really make use of higher bpc.
|
||||
|
||||
Apparently, setting max bpc to 8 breaks some displays driven by AMDGPU.
|
||||
If this happens to you, set this debug flag, which will prevent niri from changing max bpc.
|
||||
AMDGPU bug report: https://gitlab.freedesktop.org/drm/amd/-/issues/4487.
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
keep-max-bpc-unchanged
|
||||
}
|
||||
```
|
||||
|
||||
### Key Bindings
|
||||
|
||||
These are not debug options, but rather key bindings.
|
||||
@@ -37,8 +37,10 @@ input {
|
||||
// accel-speed 0.2
|
||||
// accel-profile "flat"
|
||||
// scroll-factor 1.0
|
||||
// scroll-factor vertical=1.0 horizontal=-2.0
|
||||
// scroll-method "two-finger"
|
||||
// scroll-button 273
|
||||
// scroll-button-lock
|
||||
// tap-button-map "left-middle-right"
|
||||
// click-method "clickfinger"
|
||||
// left-handed
|
||||
@@ -52,8 +54,10 @@ input {
|
||||
// accel-speed 0.2
|
||||
// accel-profile "flat"
|
||||
// scroll-factor 1.0
|
||||
// scroll-factor vertical=1.0 horizontal=-2.0
|
||||
// scroll-method "no-scroll"
|
||||
// scroll-button 273
|
||||
// scroll-button-lock
|
||||
// left-handed
|
||||
// middle-emulation
|
||||
}
|
||||
@@ -65,6 +69,7 @@ input {
|
||||
// accel-profile "flat"
|
||||
// scroll-method "on-button-down"
|
||||
// scroll-button 273
|
||||
// scroll-button-lock
|
||||
// left-handed
|
||||
// middle-emulation
|
||||
}
|
||||
@@ -76,6 +81,7 @@ input {
|
||||
// accel-profile "flat"
|
||||
// scroll-method "on-button-down"
|
||||
// scroll-button 273
|
||||
// scroll-button-lock
|
||||
// left-handed
|
||||
// middle-emulation
|
||||
}
|
||||
@@ -139,6 +145,34 @@ input {
|
||||
> }
|
||||
> ```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> <sup>Since: next release</sup>
|
||||
>
|
||||
> If the `xkb` section is empty (like it is by default), niri will fetch xkb settings from systemd-localed at `org.freedesktop.locale1` over D-Bus.
|
||||
> This way, for example, system installers can dynamically set the niri keyboard layout.
|
||||
> You can see this layout in `localectl` and change it with `localectl set-x11-keymap`, for example:
|
||||
>
|
||||
> ```sh
|
||||
> $ localectl set-x11-keymap "us" "" "colemak_dh_ortho" "compose:ralt,ctrl:nocaps"
|
||||
> $ localectl
|
||||
> System Locale: LANG=en_US.UTF-8
|
||||
> LC_NUMERIC=ru_RU.UTF-8
|
||||
> LC_TIME=ru_RU.UTF-8
|
||||
> LC_MONETARY=ru_RU.UTF-8
|
||||
> LC_PAPER=ru_RU.UTF-8
|
||||
> LC_MEASUREMENT=ru_RU.UTF-8
|
||||
> VC Keymap: us-colemak_dh_ortho
|
||||
> X11 Layout: us
|
||||
> X11 Variant: colemak_dh_ortho
|
||||
> X11 Options: compose:ralt,ctrl:nocaps
|
||||
> ```
|
||||
>
|
||||
> By default, `localectl` will set the TTY keymap to the closest match of the XKB keymap.
|
||||
> You can prevent that with a `--no-convert` flag, for example: `localectl set-x11-keymap --no-convert "us,ru"`.
|
||||
>
|
||||
> These settings are picked up by some other programs too, like GDM.
|
||||
|
||||
When using multiple layouts, niri can remember the current layout globally (the default) or per-window.
|
||||
You can control this with the `track-layout` option.
|
||||
|
||||
@@ -201,6 +235,7 @@ A few settings are common between `touchpad`, `mouse`, `trackpoint`, and `trackb
|
||||
- `scroll-method`: when to generate scroll events instead of pointer motion events, can be `no-scroll`, `two-finger`, `edge`, or `on-button-down`.
|
||||
The default and supported methods vary depending on the device type.
|
||||
- `scroll-button`: <sup>Since: 0.1.10</sup> the button code used for the `on-button-down` scroll method. You can find it in `libinput debug-events`.
|
||||
- `scroll-button-lock`: <sup>Since: next release</sup> when enabled, the button does not need to be held down. Pressing once engages scrolling, pressing a second time disengages it, and double click acts as single click of the the underlying button.
|
||||
- `left-handed`: if set, changes the device to left-handed mode.
|
||||
- `middle-emulation`: emulate a middle mouse click by pressing left and right mouse buttons at once.
|
||||
|
||||
@@ -219,6 +254,8 @@ Settings specific to `touchpad` and `mouse`:
|
||||
|
||||
- `scroll-factor`: <sup>Since: 0.1.10</sup> scales the scrolling speed by this value.
|
||||
|
||||
<sup>Since: next release</sup> You can also override horizontal and vertical scroll factor separately like so: `scroll-factor horizontal=2.0 vertical=-1.0`
|
||||
|
||||
Settings specific to `tablet`s:
|
||||
|
||||
- `calibration-matrix`: <sup>Since: 25.02</sup> set to six floating point numbers to change the calibration matrix. See the [`LIBINPUT_CALIBRATION_MATRIX` documentation](https://wayland.freedesktop.org/libinput/doc/latest/device-configuration-via-udev.html) for examples.
|
||||
@@ -206,7 +206,8 @@ binds {
|
||||
> }
|
||||
> ```
|
||||
|
||||
Currently, niri *does not* use a shell to run commands, which means that you need to manually separate arguments.
|
||||
For `spawn`, niri *does not* use a shell to run commands, which means that you need to manually separate arguments.
|
||||
See [`spawn-sh`](#spawn-sh) below for an action that uses a shell.
|
||||
|
||||
```kdl
|
||||
binds {
|
||||
@@ -249,6 +250,37 @@ binds {
|
||||
}
|
||||
```
|
||||
|
||||
#### `spawn-sh`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Run a command through the shell.
|
||||
|
||||
The argument is a single string that is passed verbatim to `sh`.
|
||||
You can use shell variables, pipelines, `~` expansion, and everything else as expected.
|
||||
|
||||
```kdl
|
||||
binds {
|
||||
// Works with spawn-sh: all arguments in the same string.
|
||||
Mod+D { spawn-sh "alacritty -e /usr/bin/fish"; }
|
||||
|
||||
// Works with spawn-sh: shell variable ($MAIN_OUTPUT), ~ expansion.
|
||||
Mod+T { spawn-sh "grim -o $MAIN_OUTPUT ~/screenshot.png"; }
|
||||
|
||||
// Works with spawn-sh: process substitution.
|
||||
Mod+Q { spawn-sh "notify-send clipboard \"$(wl-paste)\""; }
|
||||
|
||||
// Works with spawn-sh: multiple commands.
|
||||
Super+Alt+S { spawn-sh "pkill orca || exec orca"; }
|
||||
}
|
||||
```
|
||||
|
||||
`spawn-sh "some command"` is equivalent to `spawn "sh" "-c" "some command"`—it's just a less confusing shorthand.
|
||||
Keep in mind that going through the shell incurs a tiny performance penalty compared to directly `spawn`ing some binary.
|
||||
|
||||
Using `sh` is hardcoded, consistent with other compositors.
|
||||
If you want a different shell, write it out using `spawn`, e.g. `spawn "fish" "-c" "some fish command"`.
|
||||
|
||||
#### `quit`
|
||||
|
||||
Exit niri after showing a confirmation dialog to avoid accidentally triggering it.
|
||||
@@ -249,7 +249,7 @@ The difference is that the focus ring is drawn only around the active window, wh
|
||||
|
||||
| Focus Ring | Border |
|
||||
| ------------------------- | --------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
> [!TIP]
|
||||
> By default, focus ring and border are rendered as a solid background rectangle behind windows.
|
||||
@@ -338,7 +338,7 @@ Here's a visual example:
|
||||
|
||||
| Default | `relative-to="workspace-view"` |
|
||||
| -------------------------------- | --------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
```kdl
|
||||
layout {
|
||||
@@ -360,7 +360,7 @@ Supported color spaces are:
|
||||
They are rendered the same as CSS.
|
||||
For example, `active-gradient from="#f00f" to="#0f05" angle=45 in="oklch longer hue"` will look the same as CSS `linear-gradient(45deg in oklch longer hue, #f00f, #0f05)`.
|
||||
|
||||

|
||||

|
||||
|
||||
```kdl
|
||||
layout {
|
||||
@@ -516,7 +516,7 @@ layout {
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
<sup>Since: 0.1.8</sup> You can use negative values.
|
||||
They will push the windows outwards, even outside the edges of the screen.
|
||||
@@ -5,6 +5,7 @@ Here are all of these options at a glance:
|
||||
```kdl
|
||||
spawn-at-startup "waybar"
|
||||
spawn-at-startup "alacritty"
|
||||
spawn-sh-at-startup "qs -c ~/source/qs/MyAwesomeShell"
|
||||
|
||||
prefer-no-csd
|
||||
|
||||
@@ -36,12 +37,22 @@ overview {
|
||||
}
|
||||
}
|
||||
|
||||
xwayland-satellite {
|
||||
// off
|
||||
path "xwayland-satellite"
|
||||
}
|
||||
|
||||
clipboard {
|
||||
disable-primary
|
||||
}
|
||||
|
||||
hotkey-overlay {
|
||||
skip-at-startup
|
||||
hide-not-bound
|
||||
}
|
||||
|
||||
config-notification {
|
||||
disable-failed
|
||||
}
|
||||
```
|
||||
|
||||
@@ -61,6 +72,22 @@ spawn-at-startup "alacritty"
|
||||
Note that running niri as a systemd session supports xdg-desktop-autostart out of the box, which may be more convenient to use.
|
||||
Thanks to this, apps that you configured to autostart in GNOME will also "just work" in niri, without any manual `spawn-at-startup` configuration.
|
||||
|
||||
### `spawn-sh-at-startup`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Add lines like this to run shell commands at niri startup.
|
||||
|
||||
The argument is a single string that is passed verbatim to `sh`.
|
||||
You can use shell variables, pipelines, `~` expansion and everything else as expected.
|
||||
|
||||
See detailed description in the docs for the [`spawn-sh` key binding action](./Configuration:-Key-Bindings.md#spawn-sh).
|
||||
|
||||
```kdl
|
||||
// Pass all arguments in the same string.
|
||||
spawn-sh-at-startup "qs -c ~/source/qs/MyAwesomeShell"
|
||||
```
|
||||
|
||||
### `prefer-no-csd`
|
||||
|
||||
This flag will make niri ask the applications to omit their client-side decorations.
|
||||
@@ -206,6 +233,28 @@ overview {
|
||||
}
|
||||
```
|
||||
|
||||
### `xwayland-satellite`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Settings for integration with [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite).
|
||||
|
||||
When a recent enough xwayland-satellite is detected, niri will create the X11 sockets and set `DISPLAY`, then automatically spawn `xwayland-satellite` when an X11 client tries to connect.
|
||||
If Xwayland dies, niri will keep watching the X11 socket and restart `xwayland-satellite` as needed.
|
||||
This is very similar to how built-in Xwayland works in other compositors.
|
||||
|
||||
`off` disables the integration: niri won't create an X11 socket and won't set the `DISPLAY` environment variable.
|
||||
|
||||
`path` sets the path to the `xwayland-satellite` binary.
|
||||
By default, it's just `xwayland-satellite`, so it's looked up like any other non-absolute program name.
|
||||
|
||||
```kdl
|
||||
// Use a custom build of xwayland-satellite.
|
||||
xwayland-satellite {
|
||||
path "~/source/rs/xwayland-satellite/target/release/xwayland-satellite"
|
||||
}
|
||||
```
|
||||
|
||||
### `clipboard`
|
||||
|
||||
<sup>Since: 25.02</sup>
|
||||
@@ -225,6 +274,8 @@ clipboard {
|
||||
|
||||
Settings for the "Important Hotkeys" overlay.
|
||||
|
||||
#### `skip-at-startup`
|
||||
|
||||
Set the `skip-at-startup` flag if you don't want to see the hotkey help at niri startup.
|
||||
|
||||
```kdl
|
||||
@@ -233,4 +284,32 @@ hotkey-overlay {
|
||||
}
|
||||
```
|
||||
|
||||
#### `hide-not-bound`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
By default, niri will show the most important actions even if they aren't bound to any key, to prevent confusion.
|
||||
Set the `hide-not-bound` flag if you want to hide all actions not bound to any key.
|
||||
|
||||
```kdl
|
||||
hotkey-overlay {
|
||||
hide-not-bound
|
||||
}
|
||||
```
|
||||
|
||||
You can customize which binds the hotkey overlay shows using the [`hotkey-overlay-title` property](./Configuration:-Key-Bindings.md#custom-hotkey-overlay-titles).
|
||||
|
||||
### `config-notification`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Settings for the config created/failed notification.
|
||||
|
||||
Set the `disable-failed` flag to disable the "Failed to parse the config file" notification.
|
||||
For example, if you have a custom one.
|
||||
|
||||
```kdl
|
||||
config-notification {
|
||||
disable-failed
|
||||
}
|
||||
```
|
||||
@@ -285,7 +285,7 @@ window-rule {
|
||||
|
||||
Example:
|
||||
|
||||

|
||||

|
||||
|
||||
#### `is-urgent`
|
||||
|
||||
@@ -698,7 +698,7 @@ This property can be useful for rectangular windows that do not support the xdg-
|
||||
|
||||
| With Background | Without Background |
|
||||
| ------------------------------------------------ | --------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
```kdl
|
||||
window-rule {
|
||||
@@ -791,7 +791,7 @@ window-rule {
|
||||
|
||||
The radius is set in logical pixels, and controls the radius of the window itself, that is, the inner radius of the border:
|
||||
|
||||

|
||||

|
||||
|
||||
Instead of one radius, you can set four, for each corner.
|
||||
The order is the same as in CSS: top-left, top-right, bottom-right, bottom-left.
|
||||
@@ -804,7 +804,7 @@ window-rule {
|
||||
|
||||
This way, you can match GTK 3 applications which have square bottom corners:
|
||||
|
||||

|
||||

|
||||
|
||||
#### `clip-to-geometry`
|
||||
|
||||
@@ -814,7 +814,7 @@ Clips the window to its visual geometry.
|
||||
|
||||
This will cut out any client-side window shadows, and also round window corners according to `geometry-corner-radius`.
|
||||
|
||||

|
||||

|
||||
|
||||
```kdl
|
||||
window-rule {
|
||||
@@ -824,7 +824,7 @@ window-rule {
|
||||
|
||||
Enable border, set [`geometry-corner-radius`](#geometry-corner-radius) and `clip-to-geometry`, and you've got a classic setup:
|
||||
|
||||

|
||||

|
||||
|
||||
```kdl
|
||||
prefer-no-csd
|
||||
@@ -881,8 +881,12 @@ window-rule {
|
||||
}
|
||||
```
|
||||
|
||||
<video controls src="https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509">
|
||||
|
||||
https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509
|
||||
|
||||
</video>
|
||||
|
||||
#### Size Overrides
|
||||
|
||||
You can amend the window's minimum and maximum size in logical pixels.
|
||||
@@ -0,0 +1,85 @@
|
||||
niri's documentation files are found in `docs/wiki/` and should be viewable and browsable in at least three systems:
|
||||
|
||||
- The GitHub repo's markdown file preview
|
||||
- [The GitHub repo's wiki](https://github.com/YaLTeR/niri/wiki)
|
||||
- [The documentation site](https://yalter.github.io/niri/)
|
||||
|
||||
## The GitHub repo's wiki
|
||||
|
||||
This is generated with the `publish-wiki` job in `.github/workflows/ci.yml`.
|
||||
In order to have this job run as expected in your fork, you'll need to enable the wiki feature in your repo's settings on GitHub.
|
||||
This could be useful as a contributor to verify that the wiki generates the way you expect it to.
|
||||
|
||||
## The documentation site
|
||||
|
||||
The documentation site is generated with [mkdocs](https://www.mkdocs.org/).
|
||||
The configuration files are found in `docs/`.
|
||||
|
||||
To set up and run the documentation site locally, it is recommended to use [uv](https://docs.astral.sh/uv/).
|
||||
|
||||
### Serving the site locally with uv
|
||||
|
||||
In the `docs/` subdirectory:
|
||||
|
||||
- `uv sync`
|
||||
- `uv run mkdocs serve`
|
||||
|
||||
The documentation site should now be available on http://127.0.0.1:8000/niri/
|
||||
|
||||
Changes made to the documentation while the development server is running will cause an automatic page refresh in the browser.
|
||||
|
||||
> [!TIP]
|
||||
> Images may not be visible, as they are stored on Git LFS.
|
||||
> If this is the case, run `git lfs pull`.
|
||||
|
||||
## Elements
|
||||
|
||||
Elements such as links, admonitions, images, and snippets should work as expected in markdown file previews on GitHub, the GitHub repo's wiki, and in the documentation site.
|
||||
|
||||
### Links
|
||||
|
||||
Links should in all cases be relative (e.g. `./FAQ.md`), unless it's an external one.
|
||||
Links should have anchors if they are meant to lead the user to a specific section on a page (e.g. `./Getting-Started.md#nvidia`).
|
||||
|
||||
> [!TIP]
|
||||
> mkdocs will terminate if relative links lead to non-existing documents or non-existing anchors.
|
||||
> This means that the CI pipeline will fail when building documentation, as will `mkdocs serve` locally.
|
||||
|
||||
### Admonitions
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This is an important distinction from other `mkdocs`-based documentation you might have encountered.
|
||||
|
||||
Admonitions, or alerts should be written [the way GitHub defines them](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts).
|
||||
The above admonition is written like this:
|
||||
|
||||
```
|
||||
> [!IMPORTANT]
|
||||
> This is an important distinction from other `mkdocs`-based documentation you might have encountered.
|
||||
```
|
||||
|
||||
### Images
|
||||
|
||||
Images should have relative links to resources in `docs/wiki/img/`, and should contain sensible alt-text.
|
||||
|
||||
### Videos
|
||||
|
||||
For compatibility with both mkdocs and GitHub Wiki, videos need to be wrapped in a `<video>` tag (displayed by mkdocs) and have the video link again as fallback text (displayed by GitHub Wiki) padded with blank lines.
|
||||
|
||||
```html
|
||||
<video controls src="https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995">
|
||||
|
||||
https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995
|
||||
|
||||
</video>
|
||||
```
|
||||
|
||||
### Snippets
|
||||
|
||||
Configuration and code snippets in general should be annotated with a language.
|
||||
|
||||
If the language used in the snippet is KDL, open the code block like this:
|
||||
|
||||
```md
|
||||
```kdl
|
||||
```
|
||||
@@ -25,6 +25,7 @@ The way it's handled in niri is:
|
||||
It's important to understand that they remain fractional numbers in the logical space, but these numbers correspond to an integer number of pixels in the physical space.
|
||||
The rounding looks something like: `(logical_size * scale).round() / scale`.
|
||||
Whenever a workspace moves to an output with a different scale (or the output scale changes), all sizes are re-rounded from their original configured values to align with the new physical space.
|
||||
|
||||
2. The view offset and individual column/tile render offsets are *not* rounded to physical pixels, but:
|
||||
3. `tiles_with_render_positions()` rounds tile positions to physical pixels as it returns them,
|
||||
4. Custom shaders like opening, closing and resizing windows, are also careful to keep positions and sizes rounded to the physical pixels.
|
||||
@@ -2,26 +2,23 @@ When starting niri from a display manager like GDM, or otherwise through the `ni
|
||||
This provides the necessary systemd integration to run programs like `mako` and services like `xdg-desktop-portal` bound to the graphical session.
|
||||
|
||||
Here's an example on how you might set up [`mako`](https://github.com/emersion/mako), [`waybar`](https://github.com/Alexays/Waybar), [`swaybg`](https://github.com/swaywm/swaybg) and [`swayidle`](https://github.com/swaywm/swayidle) to run as systemd services with niri.
|
||||
In contrast to [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup), this lets you easily monitor their status and output, and restart or reload them.
|
||||
Unlike [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup), this lets you easily monitor their status and output, and restart or reload them.
|
||||
|
||||
1. Install them, i.e. `sudo dnf install mako waybar swaybg swayidle`
|
||||
2. Create a `niri.service.wants` folder: `mkdir -p ~/.config/systemd/user/niri.service.wants`
|
||||
|
||||
This is a special systemd folder.
|
||||
Any services linked there will be started together with `niri.service` (which is a systemd unit used by niri when running as a session).
|
||||
|
||||
3. `mako` and `waybar` provide systemd units out of the box, so you can simply symlink them into the `niri.service.wants` folder:
|
||||
2. `mako` and `waybar` provide systemd units out of the box, so you can simply add them to the niri session:
|
||||
|
||||
```
|
||||
ln -s /usr/lib/systemd/user/mako.service ~/.config/systemd/user/niri.service.wants/
|
||||
ln -s /usr/lib/systemd/user/waybar.service ~/.config/systemd/user/niri.service.wants/
|
||||
systemctl --user add-wants niri.service mako.service
|
||||
systemctl --user add-wants niri.service waybar.service
|
||||
```
|
||||
|
||||
4. `swaybg` does not provide a systemd unit, since you need to pass the background image as a command-line argument.
|
||||
This will create links in `~/.config/systemd/user/niri.service.wants/`, a special systemd folder for services that need to start together with `niri.service`.
|
||||
|
||||
3. `swaybg` does not provide a systemd unit, since you need to pass the background image as a command-line argument.
|
||||
So we will make our own.
|
||||
Put the following into `~/.config/systemd/user/swaybg.service`:
|
||||
Create `~/.config/systemd/user/swaybg.service` with the following contents:
|
||||
|
||||
```
|
||||
```systemd
|
||||
[Unit]
|
||||
PartOf=graphical-session.target
|
||||
After=graphical-session.target
|
||||
@@ -37,15 +34,16 @@ In contrast to [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-s
|
||||
|
||||
After editing `swaybg.service`, run `systemctl --user daemon-reload` so systemd picks up the changes in the file.
|
||||
|
||||
Now, also symlink this to `niri.service.wants`:
|
||||
Now, add it to the niri session:
|
||||
|
||||
```
|
||||
ln -s ~/.config/systemd/user/swaybg.service ~/.config/systemd/user/niri.service.wants/
|
||||
systemctl --user add-wants niri.service swaybg.service
|
||||
```
|
||||
|
||||
5. `swayidle` similarly does not provide a service so we will also make our own. Put the following into `~/.config/systemd/user/swayidle.service`:
|
||||
4. `swayidle` similarly does not provide a service, so we will also make our own.
|
||||
Create `~/.config/systemd/user/swayidle.service` with the following contents:
|
||||
|
||||
```
|
||||
```systemd
|
||||
[Unit]
|
||||
PartOf=graphical-session.target
|
||||
After=graphical-session.target
|
||||
@@ -56,20 +54,23 @@ In contrast to [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-s
|
||||
Restart=on-failure
|
||||
```
|
||||
|
||||
Then, run `systemctl --user daemon-reload` and symlink this file to `niri.service.wants`:
|
||||
Then, run `systemctl --user daemon-reload` and add it to the niri session:
|
||||
|
||||
```
|
||||
ln -s ~/.config/systemd/user/swayidle.service ~/.config/systemd/user/niri.service.wants/
|
||||
systemctl --user add-wants niri.service swayidle.service
|
||||
```
|
||||
|
||||
That's it!
|
||||
Now these three utilities will be started together with the niri session and stopped when it exits.
|
||||
You can also restart them with a command like `systemctl --user restart waybar.service`, for example after editing their config files.
|
||||
|
||||
To remove a service from niri startup, remove its symbolic link from `~/.config/systemd/user/niri.service.wants/`.
|
||||
Then, run `systemctl --user daemon-reload`.
|
||||
|
||||
### Running Programs Across Logout
|
||||
|
||||
When running niri as a session, exiting it (logging out) will kill all programs that you've started within. However, sometimes you want a program, like `tmux`, `dtach` or similar, to persist in this case. To do this, run it in a transient systemd scope:
|
||||
|
||||
```
|
||||
systemd-run --user --scope tmux new-session
|
||||
```
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
### How to disable client-side decorations/make windows rectangular?
|
||||
|
||||
Uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps.
|
||||
Then niri will ask windows to omit client-side decorations, and also inform them that they are being tiled (which makes some windows rectangular, even if they cannot omit the decorations).
|
||||
|
||||
Note that currently this will prevent edge window resize handles from showing up.
|
||||
You can still resize windows by holding <kbd>Mod</kbd> and the right mouse button.
|
||||
|
||||
### Why are transparent windows tinted? / Why is the border/focus ring showing up through semitransparent windows?
|
||||
|
||||
Uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps.
|
||||
Niri will draw focus rings and borders *around* windows that agree to omit their client-side decorations.
|
||||
|
||||
By default, focus ring and border are rendered as a solid background rectangle behind windows.
|
||||
That is, they will show up through semitransparent windows.
|
||||
This is because windows using client-side decorations can have an arbitrary shape.
|
||||
|
||||
You can also override this behavior with the [`draw-border-with-background` window rule](./Configuration:-Window-Rules.md#draw-border-with-background).
|
||||
|
||||
### How to enable rounded corners for all windows?
|
||||
|
||||
Put this window rule in your config:
|
||||
|
||||
```kdl
|
||||
window-rule {
|
||||
geometry-corner-radius 12
|
||||
clip-to-geometry true
|
||||
}
|
||||
```
|
||||
|
||||
For more information, check the [`geometry-corner-radius` window rule](./Configuration:-Window-Rules.md#geometry-corner-radius).
|
||||
|
||||
### How to hide the "Important Hotkeys" pop-up at the start?
|
||||
|
||||
Put this into your config:
|
||||
|
||||
```kdl
|
||||
hotkey-overlay {
|
||||
skip-at-startup
|
||||
}
|
||||
```
|
||||
|
||||
### How to run X11 apps like Steam or Discord?
|
||||
|
||||
To run X11 apps, you can use [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite).
|
||||
Check [the Xwayland wiki page](./Xwayland.md) for instructions.
|
||||
|
||||
Keep in mind that you can run many Electron apps such as VSCode natively on Wayland by passing the right flags, e.g. `code --ozone-platform-hint=auto`
|
||||
|
||||
### Why doesn't niri integrate Xwayland like other compositors?
|
||||
|
||||
A combination of factors:
|
||||
|
||||
- Integrating Xwayland is quite a bit of work, as the compositor needs to implement parts of an X11 window manager.
|
||||
- You need to appease the X11 ideas of windowing, whereas for niri I want to have the best code for Wayland.
|
||||
- niri doesn't have a good global coordinate system required by X11.
|
||||
- You tend to get an endless stream of X11 bugs that take further time and effort away from other tasks.
|
||||
- There aren't actually that many X11-only clients nowadays, and xwayland-satellite takes perfect care of most of those.
|
||||
- niri isn't a Big Serious Desktop Environment which Must Support All Use Cases (and is Backed By Some Corporation).
|
||||
|
||||
All in all, the situation works out in favor of avoiding Xwayland integration.
|
||||
|
||||
Also, in the next release niri will have seamless built-in xwayland-satellite integration, that will solve the big rough edge of having to set it up manually.
|
||||
|
||||
Besides, I wouldn't be too surprised if, down the road, xwayland-satellite becomes the standard way of integrating Xwayland into new compositors, since it takes on the bulk of the annoying work, and isolates the compositor from misbehaving clients.
|
||||
|
||||
### Can I enable blur behind semitransparent windows?
|
||||
|
||||
Not yet, follow/upvote [this issue](https://github.com/YaLTeR/niri/issues/54).
|
||||
|
||||
There's also [a PR](https://github.com/YaLTeR/niri/pull/1634) adding blur to niri which you can build and run manually.
|
||||
Keep in mind that it's an experimental implementation that may have problems and performance concerns.
|
||||
|
||||
### Can I make a window sticky / pinned / always on top / appear on all workspaces?
|
||||
|
||||
Not yet, follow/upvote [this issue](https://github.com/YaLTeR/niri/issues/932).
|
||||
|
||||
You can emulate this with a script that uses the niri IPC.
|
||||
For example, [nirius](https://git.sr.ht/~tsdh/nirius) seems to have this feature (`toggle-follow-mode`).
|
||||
|
||||
### How do I make the Bitwarden window in Firefox open as floating?
|
||||
|
||||
Firefox seems to first open the Bitwarden window with a generic Firefox title, and only later change the window title to Bitwarden, so you can't effectively target it with an `open-floating` window rule.
|
||||
|
||||
You'll need to use a script, for example [this one](https://github.com/YaLTeR/niri/discussions/1599) or other ones (search niri issues and discussions for Bitwarden).
|
||||
@@ -21,8 +21,20 @@ Also, check the [configuration introduction](./Configuration:-Introduction.md) p
|
||||
There you can find links to other pages containing thorough documentation and examples for all options.
|
||||
Finally, the [Xwayland](./Xwayland.md) page explains how to run X11 applications on niri.
|
||||
|
||||
### Desktop environments
|
||||
|
||||
Some desktop environments and shells work with niri and can give a more out-of-the-box experience:
|
||||
|
||||
- [LXQt](https://lxqt-project.org/) officially supports niri, see [their wiki](https://github.com/lxqt/lxqt/wiki/ConfigWaylandSettings#general) for details on setting it up.
|
||||
- Many [XFCE](https://www.xfce.org/) components work on Wayland, including niri. See [their wiki](https://wiki.xfce.org/releng/wayland_roadmap#component_specific_status) for details.
|
||||
- There are complete desktop shells based on Quickshell that support niri, for example [DankMaterialShell](https://github.com/AvengeMedia/DankMaterialShell) and [Noctalia](https://github.com/noctalia-dev/noctalia-shell).
|
||||
- You can run a [COSMIC](https://system76.com/cosmic/) session with niri using [cosmic-ext-extra-sessions](https://github.com/Drakulix/cosmic-ext-extra-sessions).
|
||||
|
||||
### NVIDIA
|
||||
|
||||
The NVIDIA drivers currently have an issue with high VRAM usage due to a heap reuse quirk.
|
||||
You're recommended to apply a manual fix documented [here](./Nvidia.md) if you run niri on an NVIDIA GPU.
|
||||
|
||||
NVIDIA GPUs can have problems running niri (for example, the screen remains black upon starting from a TTY).
|
||||
Sometimes, the problems can be fixed.
|
||||
You can try the following:
|
||||
@@ -50,7 +62,7 @@ You will likely have one `render` device and two `card` devices.
|
||||
|
||||
Open the niri config file at `~/.config/niri/config.kdl` and put your `render` device path like this:
|
||||
|
||||
```
|
||||
```kdl
|
||||
debug {
|
||||
render-drm-device "/dev/dri/renderD128"
|
||||
}
|
||||
@@ -156,7 +168,7 @@ We have a community-maintained flake which provides a devshell with required dep
|
||||
|
||||
If you're not on NixOS, you may need [NixGL](https://github.com/nix-community/nixGL) to run the resulting binary:
|
||||
|
||||
```
|
||||
```sh
|
||||
nix run --impure github:guibou/nixGL -- ./results/bin/niri
|
||||
```
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
### High VRAM usage fix
|
||||
|
||||
Presently, there is a quirk in the NVIDIA drivers that affects niri's VRAM usage (the driver does not properly release VRAM back into the pool). Niri *should* use on the order of 100 MiB of VRAM (as checked in [nvtop](https://github.com/Syllo/nvtop)); if you see anywhere close to 1 GiB of VRAM in use, you are likely hitting this issue (heap not returning freed buffers to the driver).
|
||||
|
||||
Luckily, you can mitigate this by configuring the NVIDIA drivers with a per-process application profile as follows:
|
||||
|
||||
* `sudo mkdir -p /etc/nvidia/nvidia-application-profiles-rc.d` to make the config dir if it does not exist (it most likely does not if you are reading this)
|
||||
* write the following JSON blob to set the `GLVidHeapReuseRatio` config value for the `niri` process into the file `/etc/nvidia/nvidia-application-profiles-rc.d/50-limit-free-buffer-pool-in-wayland-compositors.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"pattern": {
|
||||
"feature": "procname",
|
||||
"matches": "niri"
|
||||
},
|
||||
"profile": "Limit Free Buffer Pool On Wayland Compositors"
|
||||
}
|
||||
],
|
||||
"profiles": [
|
||||
{
|
||||
"name": "Limit Free Buffer Pool On Wayland Compositors",
|
||||
"settings": [
|
||||
{
|
||||
"key": "GLVidHeapReuseRatio",
|
||||
"value": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
(The file in `/etc/nvidia/nvidia-application-profiles-rc.d/` can be named anything, and does not actually need an extension).
|
||||
|
||||
Restart niri after writing the config file to apply the change.
|
||||
|
||||
The upstream issue that this solution was pulled from is [here](https://github.com/NVIDIA/egl-wayland/issues/126#issuecomment-2379945259). There is a (slim) chance that NVIDIA updates their built-in application profiles to apply this to niri automatically; it is unlikely that the underlying heuristic will see a proper fix.
|
||||
|
||||
The fix shipped in the driver at the time of writing uses a value of 0, while the initial config posted by an Nvidia engineer approximately a year prior used a value of 1.
|
||||
|
||||
### Screencast flickering fix
|
||||
|
||||
<sup>Until: next release</sup>
|
||||
|
||||
If you have screencast glitches or flickering on NVIDIA, set this in the niri config:
|
||||
|
||||
```kdl,must-fail
|
||||
debug {
|
||||
wait-for-frame-completion-in-pipewire
|
||||
}
|
||||
```
|
||||
|
||||
This will become unnecessary once niri supports explicit sync for PipeWire screencasts: https://github.com/YaLTeR/niri/issues/1432
|
||||
@@ -5,8 +5,12 @@
|
||||
The Overview is a zoomed-out view of your workspaces and windows.
|
||||
It lets you see what's going on at a glance, navigate, and drag windows around.
|
||||
|
||||
<video controls src="https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995">
|
||||
|
||||
https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995
|
||||
|
||||
</video>
|
||||
|
||||
Open it with the `toggle-overview` bind, via the top-left hot corner, or using a touchpad four-finger swipe up.
|
||||
While in the overview, all keyboard shortcuts keep working, while pointing devices get easier:
|
||||
|
||||
@@ -23,12 +27,20 @@ While in the overview, all keyboard shortcuts keep working, while pointing devic
|
||||
Drag-and-drop will scroll the workspaces up/down in the overview, and will activate a workspace when holding it for a moment.
|
||||
Combined with the hot corner, this lets you do a mouse-only DnD across workspaces.
|
||||
|
||||
<video controls src="https://github.com/user-attachments/assets/5f09c5b7-ff40-462b-8b9c-f1b8073a2cbb">
|
||||
|
||||
https://github.com/user-attachments/assets/5f09c5b7-ff40-462b-8b9c-f1b8073a2cbb
|
||||
|
||||
</video>
|
||||
|
||||
You can also drag-and-drop a window to a new workspace above, below, or between existing workspaces.
|
||||
|
||||
<video controls src="https://github.com/user-attachments/assets/b76d5349-aa20-4889-ab90-0a51554c789d">
|
||||
|
||||
https://github.com/user-attachments/assets/b76d5349-aa20-4889-ab90-0a51554c789d
|
||||
|
||||
</video>
|
||||
|
||||
### Configuration
|
||||
|
||||
See the full documentation for the `overview {}` section [here](./Configuration:-Miscellaneous.md#overview).
|
||||
@@ -0,0 +1,15 @@
|
||||
Welcome to the niri documentation!
|
||||
|
||||
Feel free to look through usage and [Getting started](./Getting-Started.md).
|
||||
If you're looking for ways to configure niri, check out the [introduction to configuration](./Configuration:-Introduction.md).
|
||||
|
||||
If you'd like to help with niri, there are plenty of both coding- and non-coding-related ways to do so.
|
||||
See [CONTRIBUTING.md](https://github.com/YaLTeR/niri/blob/main/CONTRIBUTING.md) for an overview.
|
||||
|
||||
If you're not already here, check out our new wiki website! https://yalter.github.io/niri/
|
||||
|
||||
---
|
||||
|
||||
The documentation is open to contribution, see [Documenting niri](./Development:-Documenting-niri.md).
|
||||
Please discuss bigger changes in [our Matrix room](https://matrix.to/#/#niri:matrix.org) first!
|
||||
The wiki is generated from files in the `docs/wiki/` folder of the repository, so you can open a pull request modifying it there.
|
||||
@@ -66,8 +66,12 @@ You can also use these actions from the command line, for example to interactive
|
||||
$ niri msg action set-dynamic-cast-window --id $(niri msg --json pick-window | jq .id)
|
||||
```
|
||||
|
||||
<video controls src="https://github.com/user-attachments/assets/c617a9d6-7d5e-4f1f-b8cc-9301182d9634">
|
||||
|
||||
https://github.com/user-attachments/assets/c617a9d6-7d5e-4f1f-b8cc-9301182d9634
|
||||
|
||||
</video>
|
||||
|
||||
If the cast target disappears (e.g. the target window closes), the stream goes back to empty.
|
||||
|
||||
All dynamic casts share the same target, but new ones start out empty until the next time you change it (to avoid surprises and sharing something sensitive by mistake).
|
||||
@@ -76,10 +80,10 @@ All dynamic casts share the same target, but new ones start out empty until the
|
||||
|
||||
<sup>Since: 25.02</sup>
|
||||
|
||||
The [`is-window-cast-target=true` window rule](./Configuration:-Window-Rules.md#is-window-cast-target) matches windows targetted by an ongoing window screencast.
|
||||
The [`is-window-cast-target=true` window rule](./Configuration:-Window-Rules.md#is-window-cast-target) matches windows targeted by an ongoing window screencast.
|
||||
You use it with a special border color to clearly indicate screencasted windows.
|
||||
|
||||
This also works for windows targetted by dynamic screencasts.
|
||||
This also works for windows targeted by dynamic screencasts.
|
||||
However, it will not work for windows that just happen to be visible in a full-monitor screencast.
|
||||
|
||||
```kdl
|
||||
@@ -32,4 +32,4 @@ This is especially useful for thicker tab indicators, or when you have very smal
|
||||
|
||||
| Default | `place-within-column` |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
@@ -1,11 +1,20 @@
|
||||
X11 is very cursed, so built-in Xwayland support is not planned at the moment.
|
||||
X11 is very cursed, so built-in Xwayland support [is not planned at the moment](./FAQ.md#why-doesnt-niri-integrate-xwayland-like-other-compositors).
|
||||
However, there are multiple solutions to running X11 apps in niri.
|
||||
|
||||
## Using xwayland-satellite
|
||||
|
||||
[xwayland-satellite] implements rootless Xwayland in a separate application, without the host compositor's involvement.
|
||||
It makes X11 windows appear as normal windows, just like a native Xwayland integration.
|
||||
While it is still somewhat experimental, it handles a lot of applications correctly, like Steam, games and Discord.
|
||||
xwayland-satellite works well with most applications: Steam, games, Discord, even more exotic things like Ardour with wine Windows VST plugins.
|
||||
However, X11 apps that want to position windows or bars at specific screen coordinates won't behave correctly.
|
||||
|
||||
> [!NOTE]
|
||||
> In the next release, niri will have [built-in xwayland-satellite integration](./Configuration:-Miscellaneous.md#xwayland-satellite).
|
||||
> You can try it by installing git versions of both niri and xwayland-satellite.
|
||||
> With no further configuration, niri will create X11 sockets, then when an X11 client connects, automatically start xwayland-satellite.
|
||||
>
|
||||
> This matches how other compositors run Xwayland (but in niri's case, it's xwayland-satellite rather than Xwayland itself).
|
||||
> It also makes X11 apps work fine in `spawn-at-startup` and in XDG autostart.
|
||||
|
||||
Install it from your package manager, or build it according to instructions from its README, then run the `xwayland-satellite` binary.
|
||||
Look for a log message like: `Connected to Xwayland on :0`.
|
||||
@@ -10,6 +10,7 @@
|
||||
* [Layer‐Shell Components](./Layer%E2%80%90Shell-Components.md)
|
||||
* [IPC, `niri msg`](./IPC.md)
|
||||
* [Application-Specific Issues](./Application-Issues.md)
|
||||
* [Nvidia](./Nvidia.md)
|
||||
* [Xwayland](./Xwayland.md)
|
||||
* [Gestures](./Gestures.md)
|
||||
* [Packaging niri](./Packaging-niri.md)
|
||||
@@ -33,6 +34,7 @@
|
||||
## Development
|
||||
* [Design Principles](./Development:-Design-Principles.md)
|
||||
* [Developing niri](./Development:-Developing-niri.md)
|
||||
* [Documenting niri](./Development:-Documenting-niri.md)
|
||||
* [Fractional Layout](./Development:-Fractional-Layout.md)
|
||||
* [Redraw Loop](./Development:-Redraw-Loop.md)
|
||||
* [Animation Timing](./Development:-Animation-Timing.md)
|
||||
@@ -0,0 +1,405 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512.00006"
|
||||
viewBox="0 0 135.46667 135.46668"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:zoom="0.8030909"
|
||||
inkscape:cx="26.14897"
|
||||
inkscape:cy="535.43129"
|
||||
inkscape:window-width="2528"
|
||||
inkscape:window-height="1408"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g71" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter21"
|
||||
x="-0.35129357"
|
||||
y="-0.93287114"
|
||||
width="1.7025871"
|
||||
height="2.8657423">
|
||||
<feGaussianBlur
|
||||
stdDeviation="5.0792937"
|
||||
id="feGaussianBlur21" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter22"
|
||||
x="-0.42891072"
|
||||
y="-0.64938287"
|
||||
width="1.8578214"
|
||||
height="2.2987657">
|
||||
<feGaussianBlur
|
||||
stdDeviation="6.5904956"
|
||||
id="feGaussianBlur22" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter23"
|
||||
x="-0.41511482"
|
||||
y="-0.64325018"
|
||||
width="1.8302296"
|
||||
height="2.2865004">
|
||||
<feGaussianBlur
|
||||
stdDeviation="7.8584288"
|
||||
id="feGaussianBlur23" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter24"
|
||||
x="-0.36140346"
|
||||
y="-0.92623631"
|
||||
width="1.7228069"
|
||||
height="2.8524726">
|
||||
<feGaussianBlur
|
||||
stdDeviation="5.269127"
|
||||
id="feGaussianBlur24" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter27"
|
||||
x="-0.17576354"
|
||||
y="-0.83601771"
|
||||
width="1.3515271"
|
||||
height="2.6720354">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.753432"
|
||||
id="feGaussianBlur26" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter52"
|
||||
x="-0.33025388"
|
||||
y="-1.0968854"
|
||||
width="1.6605078"
|
||||
height="3.1937708">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.2575558"
|
||||
id="feGaussianBlur52" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter53"
|
||||
x="-0.34014132"
|
||||
y="-1.0243618"
|
||||
width="1.6802826"
|
||||
height="3.0487236">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.3187927"
|
||||
id="feGaussianBlur54" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter55"
|
||||
x="-0.33171227"
|
||||
y="-1.0929411"
|
||||
width="1.6634245"
|
||||
height="3.1858823">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.4263355"
|
||||
id="feGaussianBlur55" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter56"
|
||||
x="-0.34596671"
|
||||
y="-0.98818076"
|
||||
width="1.6919334"
|
||||
height="2.9763615">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.9267071"
|
||||
id="feGaussianBlur56" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter57"
|
||||
x="-0.50604737"
|
||||
y="-0.53225252"
|
||||
width="2.0120947"
|
||||
height="2.064505">
|
||||
<feGaussianBlur
|
||||
stdDeviation="5.8261111"
|
||||
id="feGaussianBlur57" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter58"
|
||||
x="-0.32130078"
|
||||
y="-1.1307663"
|
||||
width="1.6426016"
|
||||
height="3.2615325">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.2390405"
|
||||
id="feGaussianBlur58" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter59"
|
||||
x="-0.4030115"
|
||||
y="-0.71156066"
|
||||
width="1.806023"
|
||||
height="2.4231213">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.4950086"
|
||||
id="feGaussianBlur59" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter60"
|
||||
x="-0.1759294"
|
||||
y="-1.0177407"
|
||||
width="1.3518588"
|
||||
height="3.0354814">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.8003951"
|
||||
id="feGaussianBlur60" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter70"
|
||||
x="-0.43159762"
|
||||
y="-0.3122953"
|
||||
width="1.8631952"
|
||||
height="1.6245906">
|
||||
<feGaussianBlur
|
||||
stdDeviation="18.461288"
|
||||
id="feGaussianBlur70" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter72"
|
||||
x="-0.43271319"
|
||||
y="-0.31310251"
|
||||
width="1.8654264"
|
||||
height="1.626205">
|
||||
<feGaussianBlur
|
||||
stdDeviation="18.509006"
|
||||
id="feGaussianBlur72" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
x="-0.45705071"
|
||||
y="-0.57220236"
|
||||
width="1.9141014"
|
||||
height="2.1444047">
|
||||
<feGaussianBlur
|
||||
stdDeviation="8.5810337"
|
||||
id="feGaussianBlur1" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter2"
|
||||
x="-0.545201"
|
||||
y="-0.88686217"
|
||||
width="2.090402"
|
||||
height="2.7737243">
|
||||
<feGaussianBlur
|
||||
stdDeviation="4.3199889"
|
||||
id="feGaussianBlur2" />
|
||||
</filter>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath1">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 452.78682,224.36663 q -8.98432,0 -12.80266,-4.94138 -3.59373,-5.16598 -4.26757,-12.80265 -0.67381,-7.86128 -0.67381,-15.72256 0,-11.90423 -3.36913,-19.09168 -3.36912,-7.41207 -11.90421,-7.41207 -7.63667,0 -11.2304,7.41207 -3.59373,7.18745 -3.59373,19.09168 0,7.86128 -0.67384,15.49795 -0.44921,7.63667 -4.04294,12.80266 -3.59373,4.94137 -13.02726,4.94137 -6.96286,0 -11.67961,-5.16598 -4.49217,-5.16599 -6.96285,-13.47648 -2.47068,-8.3105 -3.59373,-17.96864 -0.89842,-9.65815 -0.89842,-18.64247 0,-8.98432 0.89842,-18.41785 0.89844,-9.43354 3.14452,-17.51943 2.47068,-8.08588 6.96285,-13.02726 4.49215,-4.94138 11.67961,-4.94138 12.80266,0 15.94715,11.67962 2.69531,-2.9199 8.53512,-5.39059 6.0644,-2.6953 13.70107,-2.6953 17.29484,0 28.30063,8.75971 11.00577,8.75972 15.94715,24.25767 5.16599,15.27334 4.94139,35.48806 0,10.33197 -2.02147,18.86707 -1.79687,8.3105 -6.51365,13.47648 -4.49215,4.94138 -12.80263,4.94138 z"
|
||||
id="path5" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath5">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 452.78682,224.36663 q -8.98432,0 -12.80266,-4.94138 -3.59373,-5.16598 -4.26757,-12.80265 -0.67381,-7.86128 -0.67381,-15.72256 0,-11.90423 -3.36913,-19.09168 -3.36912,-7.41207 -11.90421,-7.41207 -7.63667,0 -11.2304,7.41207 -3.59373,7.18745 -3.59373,19.09168 0,7.86128 -0.67384,15.49795 -0.44921,7.63667 -4.04294,12.80266 -3.59373,4.94137 -13.02726,4.94137 -6.96286,0 -11.67961,-5.16598 -4.49217,-5.16599 -6.96285,-13.47648 -2.47068,-8.3105 -3.59373,-17.96864 -0.89842,-9.65815 -0.89842,-18.64247 0,-8.98432 0.89842,-18.41785 0.89844,-9.43354 3.14452,-17.51943 2.47068,-8.08588 6.96285,-13.02726 4.49215,-4.94138 11.67961,-4.94138 12.80266,0 15.94715,11.67962 2.69531,-2.9199 8.53512,-5.39059 6.0644,-2.6953 13.70107,-2.6953 17.29484,0 28.30063,8.75971 11.00577,8.75972 15.94715,24.25767 5.16599,15.27334 4.94139,35.48806 0,10.33197 -2.02147,18.86707 -1.79687,8.3105 -6.51365,13.47648 -4.49215,4.94138 -12.80263,4.94138 z"
|
||||
id="path8" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath8">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 452.78682,224.36663 q -8.98432,0 -12.80266,-4.94138 -3.59373,-5.16598 -4.26757,-12.80265 -0.67381,-7.86128 -0.67381,-15.72256 0,-11.90423 -3.36913,-19.09168 -3.36912,-7.41207 -11.90421,-7.41207 -7.63667,0 -11.2304,7.41207 -3.59373,7.18745 -3.59373,19.09168 0,7.86128 -0.67384,15.49795 -0.44921,7.63667 -4.04294,12.80266 -3.59373,4.94137 -13.02726,4.94137 -6.96286,0 -11.67961,-5.16598 -4.49217,-5.16599 -6.96285,-13.47648 -2.47068,-8.3105 -3.59373,-17.96864 -0.89842,-9.65815 -0.89842,-18.64247 0,-8.98432 0.89842,-18.41785 0.89844,-9.43354 3.14452,-17.51943 2.47068,-8.08588 6.96285,-13.02726 4.49215,-4.94138 11.67961,-4.94138 12.80266,0 15.94715,11.67962 2.69531,-2.9199 8.53512,-5.39059 6.0644,-2.6953 13.70107,-2.6953 17.29484,0 28.30063,8.75971 11.00577,8.75972 15.94715,24.25767 5.16599,15.27334 4.94139,35.48806 0,10.33197 -2.02147,18.86707 -1.79687,8.3105 -6.51365,13.47648 -4.49215,4.94138 -12.80263,4.94138 z"
|
||||
id="path11" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath11">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 452.78682,224.36663 q -8.98432,0 -12.80266,-4.94138 -3.59373,-5.16598 -4.26757,-12.80265 -0.67381,-7.86128 -0.67381,-15.72256 0,-11.90423 -3.36913,-19.09168 -3.36912,-7.41207 -11.90421,-7.41207 -7.63667,0 -11.2304,7.41207 -3.59373,7.18745 -3.59373,19.09168 0,7.86128 -0.67384,15.49795 -0.44921,7.63667 -4.04294,12.80266 -3.59373,4.94137 -13.02726,4.94137 -6.96286,0 -11.67961,-5.16598 -4.49217,-5.16599 -6.96285,-13.47648 -2.47068,-8.3105 -3.59373,-17.96864 -0.89842,-9.65815 -0.89842,-18.64247 0,-8.98432 0.89842,-18.41785 0.89844,-9.43354 3.14452,-17.51943 2.47068,-8.08588 6.96285,-13.02726 4.49215,-4.94138 11.67961,-4.94138 12.80266,0 15.94715,11.67962 2.69531,-2.9199 8.53512,-5.39059 6.0644,-2.6953 13.70107,-2.6953 17.29484,0 28.30063,8.75971 11.00577,8.75972 15.94715,24.25767 5.16599,15.27334 4.94139,35.48806 0,10.33197 -2.02147,18.86707 -1.79687,8.3105 -6.51365,13.47648 -4.49215,4.94138 -12.80263,4.94138 z"
|
||||
id="path12" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 452.78682,224.36663 q -8.98432,0 -12.80266,-4.94138 -3.59373,-5.16598 -4.26757,-12.80265 -0.67381,-7.86128 -0.67381,-15.72256 0,-11.90423 -3.36913,-19.09168 -3.36912,-7.41207 -11.90421,-7.41207 -7.63667,0 -11.2304,7.41207 -3.59373,7.18745 -3.59373,19.09168 0,7.86128 -0.67384,15.49795 -0.44921,7.63667 -4.04294,12.80266 -3.59373,4.94137 -13.02726,4.94137 -6.96286,0 -11.67961,-5.16598 -4.49217,-5.16599 -6.96285,-13.47648 -2.47068,-8.3105 -3.59373,-17.96864 -0.89842,-9.65815 -0.89842,-18.64247 0,-8.98432 0.89842,-18.41785 0.89844,-9.43354 3.14452,-17.51943 2.47068,-8.08588 6.96285,-13.02726 4.49215,-4.94138 11.67961,-4.94138 12.80266,0 15.94715,11.67962 2.69531,-2.9199 8.53512,-5.39059 6.0644,-2.6953 13.70107,-2.6953 17.29484,0 28.30063,8.75971 11.00577,8.75972 15.94715,24.25767 5.16599,15.27334 4.94139,35.48806 0,10.33197 -2.02147,18.86707 -1.79687,8.3105 -6.51365,13.47648 -4.49215,4.94138 -12.80263,4.94138 z"
|
||||
id="path14" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath15">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 511.63431,224.36663 q -8.98433,0 -13.47647,-5.16598 -4.26755,-5.16599 -5.83981,-15.72256 -1.34765,-10.78119 -1.57226,-27.62679 0,-16.8456 1.34763,-27.62678 1.57226,-10.78119 6.06444,-15.72256 4.49214,-5.16599 13.47647,-5.16599 8.98432,0 13.25187,5.16599 4.49217,4.94137 6.06443,15.72256 1.57223,10.78118 1.57223,27.62678 0.22461,16.8456 -1.34762,27.62679 -1.57226,10.55657 -6.06444,15.72256 -4.49215,5.16598 -13.47647,5.16598 z"
|
||||
id="path16" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath17">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 681.32926,224.3022 q -8.98433,0 -13.47647,-5.16597 -4.26755,-5.16599 -5.83981,-15.72257 -1.34765,-10.78118 -1.57226,-27.62678 0,-16.8456 1.34763,-27.62678 1.57226,-10.78119 6.06444,-15.72257 4.49214,-5.16598 13.47647,-5.16598 8.98432,0 13.25187,5.16598 4.49217,4.94138 6.06443,15.72257 1.57223,10.78118 1.57223,27.62678 0.22461,16.8456 -1.34762,27.62678 -1.57226,10.55658 -6.06444,15.72257 -4.49215,5.16597 -13.47647,5.16597 z"
|
||||
id="path17" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 573.177,224.36663 q -7.63667,0 -12.57805,-4.94138 -4.71678,-5.16598 -7.18746,-13.70108 -2.47068,-8.53511 -3.36913,-19.5409 -0.89842,-11.00579 -0.89842,-22.68541 0,-7.86128 0.22461,-15.94717 0.44921,-8.31049 2.24607,-15.27334 2.02147,-6.96285 6.51362,-11.2304 4.71678,-4.49216 13.25187,-4.49216 6.06443,0 11.00582,2.9199 4.94136,2.6953 6.28901,8.3105 5.16599,-4.71677 13.02727,-6.73824 8.08587,-2.24608 16.62099,-2.24608 7.18746,0 14.59952,3.59373 7.41207,3.36912 12.57803,9.43353 5.16599,6.06442 5.16599,13.70109 0,6.51363 -4.94136,12.35344 -4.71678,5.83981 -13.4765,5.83981 -3.81831,0 -6.28901,-0.89843 -2.47068,-1.12304 -4.71676,-2.02147 -2.2461,-0.89844 -5.39059,-0.89844 -8.31051,0 -15.27337,6.73824 -6.73822,6.51364 -6.73822,17.74404 0,5.8398 -0.44921,12.80265 -0.22463,6.73824 -2.02147,13.02727 -1.57226,6.28902 -5.83983,10.33196 -4.26754,3.81834 -12.35342,3.81834 z"
|
||||
id="path19" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath19">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 573.177,224.36663 q -7.63667,0 -12.57805,-4.94138 -4.71678,-5.16598 -7.18746,-13.70108 -2.47068,-8.53511 -3.36913,-19.5409 -0.89842,-11.00579 -0.89842,-22.68541 0,-7.86128 0.22461,-15.94717 0.44921,-8.31049 2.24607,-15.27334 2.02147,-6.96285 6.51362,-11.2304 4.71678,-4.49216 13.25187,-4.49216 6.06443,0 11.00582,2.9199 4.94136,2.6953 6.28901,8.3105 5.16599,-4.71677 13.02727,-6.73824 8.08587,-2.24608 16.62099,-2.24608 7.18746,0 14.59952,3.59373 7.41207,3.36912 12.57803,9.43353 5.16599,6.06442 5.16599,13.70109 0,6.51363 -4.94136,12.35344 -4.71678,5.83981 -13.4765,5.83981 -3.81831,0 -6.28901,-0.89843 -2.47068,-1.12304 -4.71676,-2.02147 -2.2461,-0.89844 -5.39059,-0.89844 -8.31051,0 -15.27337,6.73824 -6.73822,6.51364 -6.73822,17.74404 0,5.8398 -0.44921,12.80265 -0.22463,6.73824 -2.02147,13.02727 -1.57226,6.28902 -5.83983,10.33196 -4.26754,3.81834 -12.35342,3.81834 z"
|
||||
id="path20" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath20">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 573.177,224.36663 q -7.63667,0 -12.57805,-4.94138 -4.71678,-5.16598 -7.18746,-13.70108 -2.47068,-8.53511 -3.36913,-19.5409 -0.89842,-11.00579 -0.89842,-22.68541 0,-7.86128 0.22461,-15.94717 0.44921,-8.31049 2.24607,-15.27334 2.02147,-6.96285 6.51362,-11.2304 4.71678,-4.49216 13.25187,-4.49216 6.06443,0 11.00582,2.9199 4.94136,2.6953 6.28901,8.3105 5.16599,-4.71677 13.02727,-6.73824 8.08587,-2.24608 16.62099,-2.24608 7.18746,0 14.59952,3.59373 7.41207,3.36912 12.57803,9.43353 5.16599,6.06442 5.16599,13.70109 0,6.51363 -4.94136,12.35344 -4.71678,5.83981 -13.4765,5.83981 -3.81831,0 -6.28901,-0.89843 -2.47068,-1.12304 -4.71676,-2.02147 -2.2461,-0.89844 -5.39059,-0.89844 -8.31051,0 -15.27337,6.73824 -6.73822,6.51364 -6.73822,17.74404 0,5.8398 -0.44921,12.80265 -0.22463,6.73824 -2.02147,13.02727 -1.57226,6.28902 -5.83983,10.33196 -4.26754,3.81834 -12.35342,3.81834 z"
|
||||
id="path21" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath21">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 573.177,224.36663 q -7.63667,0 -12.57805,-4.94138 -4.71678,-5.16598 -7.18746,-13.70108 -2.47068,-8.53511 -3.36913,-19.5409 -0.89842,-11.00579 -0.89842,-22.68541 0,-7.86128 0.22461,-15.94717 0.44921,-8.31049 2.24607,-15.27334 2.02147,-6.96285 6.51362,-11.2304 4.71678,-4.49216 13.25187,-4.49216 6.06443,0 11.00582,2.9199 4.94136,2.6953 6.28901,8.3105 5.16599,-4.71677 13.02727,-6.73824 8.08587,-2.24608 16.62099,-2.24608 7.18746,0 14.59952,3.59373 7.41207,3.36912 12.57803,9.43353 5.16599,6.06442 5.16599,13.70109 0,6.51363 -4.94136,12.35344 -4.71678,5.83981 -13.4765,5.83981 -3.81831,0 -6.28901,-0.89843 -2.47068,-1.12304 -4.71676,-2.02147 -2.2461,-0.89844 -5.39059,-0.89844 -8.31051,0 -15.27337,6.73824 -6.73822,6.51364 -6.73822,17.74404 0,5.8398 -0.44921,12.80265 -0.22463,6.73824 -2.02147,13.02727 -1.57226,6.28902 -5.83983,10.33196 -4.26754,3.81834 -12.35342,3.81834 z"
|
||||
id="path22" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath23">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 681.43807,224.36663 q -8.98432,0 -13.47647,-5.16598 -4.26757,-5.16599 -5.83983,-15.72256 -1.34763,-10.78119 -1.57226,-27.62679 0,-16.8456 1.34765,-27.62678 1.57226,-10.78119 6.06444,-15.72256 4.49215,-5.16599 13.47647,-5.16599 8.98432,0 13.25187,5.16599 4.49215,4.94137 6.06441,15.72256 1.57226,10.78118 1.57226,27.62678 0.2246,16.8456 -1.34766,27.62679 -1.57223,10.55657 -6.06441,15.72256 -4.49215,5.16598 -13.47647,5.16598 z"
|
||||
id="path25" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath25">
|
||||
<path
|
||||
style="font-size:224.608px;line-height:393.415px;font-family:'Cherry Bomb One';-inkscape-font-specification:'Cherry Bomb One';letter-spacing:0px;fill:#259eb4;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 681.43807,224.36663 q -8.98432,0 -13.47647,-5.16598 -4.26757,-5.16599 -5.83983,-15.72256 -1.34763,-10.78119 -1.57226,-27.62679 0,-16.8456 1.34765,-27.62678 1.57226,-10.78119 6.06444,-15.72256 4.49215,-5.16599 13.47647,-5.16599 8.98432,0 13.25187,5.16599 4.49215,4.94137 6.06441,15.72256 1.57226,10.78118 1.57226,27.62678 0.2246,16.8456 -1.34766,27.62679 -1.57223,10.55657 -6.06441,15.72256 -4.49215,5.16598 -13.47647,5.16598 z"
|
||||
id="path26" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath34">
|
||||
<path
|
||||
id="path35"
|
||||
style="fill:#259eb4;fill-opacity:1;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 169.33334,220.13334 c 0,8.46667 -4.23334,16.93333 -33.86667,16.93333 -29.63333,0 -33.86667,-8.46666 -33.86667,-16.93333 0,-12.7 8.46667,-21.16667 33.86667,-21.16667 25.4,0 33.86667,8.46667 33.86667,21.16667 z" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath35">
|
||||
<path
|
||||
id="path36"
|
||||
style="fill:#259eb4;fill-opacity:1;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 715.91343,141.3418 c 0,8.46667 -4.23334,16.93332 -33.86667,16.93332 -29.63333,0 -33.86667,-8.46665 -33.86667,-16.93332 0,-12.7 8.46667,-21.16668 33.86667,-21.16668 25.4,0 33.86667,8.46668 33.86667,21.16668 z" />
|
||||
</clipPath>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter61"
|
||||
x="-0.43233622"
|
||||
y="-0.31282974"
|
||||
width="1.8646724"
|
||||
height="1.6256595">
|
||||
<feGaussianBlur
|
||||
stdDeviation="18.492881"
|
||||
id="feGaussianBlur61" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(255.60477,-19.034522)">
|
||||
<g
|
||||
id="g71"
|
||||
transform="matrix(0.53845513,0,0,0.53845513,-260.51955,13.25883)"
|
||||
inkscape:label="ICON">
|
||||
<g
|
||||
id="g4"
|
||||
inkscape:label="OUTLINE">
|
||||
<path
|
||||
id="path1"
|
||||
style="display:inline;fill:#2d2d2d;fill-opacity:1;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 131.35535,193.41317 c -8.42529,0.21555 -17.07195,1.23898 -24.70899,5.03516 -6.56852,3.28887 -11.780948,9.56904 -13.037575,16.89416 -1.12503,5.50318 -0.847502,11.50687 1.819693,16.54193 2.619641,4.74577 7.282642,8.08459 12.343352,9.82958 7.20278,2.70364 14.97875,3.20323 22.59699,3.46523 7.89843,0.20478 15.85819,-0.17121 23.61105,-1.76836 5.63625,-1.25422 11.31076,-3.51115 15.35105,-7.77992 3.29144,-3.52234 5.05422,-8.32066 5.11255,-13.1185 0.31661,-6.70741 -1.69285,-13.72981 -6.39837,-18.65616 -4.18578,-4.5464 -10.04779,-7.11576 -15.95626,-8.569 -6.77112,-1.66654 -13.79057,-1.92415 -20.73349,-1.87412 z" />
|
||||
<path
|
||||
id="path4"
|
||||
style="display:inline;mix-blend-mode:normal;fill:#2d2d2d;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="m 98.902224,36.772549 c -6.995893,0.126233 -13.62619,4.553849 -16.638172,10.834978 -3.470673,6.368967 -4.476788,13.68672 -5.225527,20.799337 -1.546324,15.903589 -0.51861,31.905986 -1.354082,47.842876 -0.544064,12.91133 -1.490643,25.96714 0.724426,38.77934 1.594594,9.15149 5.382372,18.1514 11.945332,24.8552 5.649973,5.83947 12.892719,10.17523 20.761639,12.23377 4.29395,0.94959 9.28733,-0.57303 11.73776,-4.36606 2.38164,-3.59662 2.34796,-8.32076 0.82613,-12.24486 -1.20789,-3.11502 -2.64307,-6.14315 -3.64842,-9.33584 -1.12185,-3.39459 -1.94228,-6.99797 -2.3869,-10.47882 -0.55748,-4.05967 -0.49766,-8.22945 -0.15435,-12.24032 0.58298,-6.27022 1.46357,-12.5645 3.39036,-18.57849 0.79153,-2.26867 1.52928,-4.64049 3.02766,-6.55814 5.54574,0.77098 10.63142,3.47347 15.10865,6.73077 1.80507,1.34294 3.49258,2.79574 5.00233,4.33482 4.14952,4.21383 7.69916,9.25861 9.14438,15.0671 1.23316,4.77068 1.46924,9.85005 0.41871,14.67703 -1.02032,4.50982 -3.25363,8.60065 -5.45452,12.62181 -1.87577,3.646 -3.05511,7.8744 -2.17051,11.96837 0.96209,4.20638 4.82507,7.5927 9.1658,7.8093 4.52117,0.38592 8.79301,-1.6188 12.64582,-3.75368 9.91038,-5.51624 17.57193,-14.7796 21.44242,-25.41236 3.56981,-9.82442 4.74142,-20.45137 3.89537,-30.84701 -1.23706,-11.29301 -6.03879,-21.87272 -12.00583,-31.42104 -8.7119,-13.884894 -20.08721,-25.908588 -32.38582,-36.666079 -9.92644,-8.582365 -20.62052,-16.394651 -32.3549,-22.32888 -4.66406,-2.261816 -9.65523,-4.278849 -14.913576,-4.33312 -0.18139,0.0033 -0.36279,0.0067 -0.54418,0.01 z" />
|
||||
</g>
|
||||
<g
|
||||
id="g26"
|
||||
clip-path="none"
|
||||
style="display:inline"
|
||||
inkscape:label="STUB">
|
||||
<path
|
||||
id="path2"
|
||||
style="fill:#259eb4;fill-opacity:1;stroke-width:2.11667;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="m 169.33334,220.13334 c 0,8.46667 -4.23334,16.93333 -33.86667,16.93333 -29.63333,0 -33.86667,-8.46666 -33.86667,-16.93333 0,-12.7 8.46667,-21.16667 33.86667,-21.16667 25.4,0 33.86667,8.46667 33.86667,21.16667 z" />
|
||||
<path
|
||||
style="mix-blend-mode:normal;fill:#92cfda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.292;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;filter:url(#filter27)"
|
||||
d="m 649.45734,133.81724 c 0.30861,-0.52382 1.24567,-1.10943 2.25778,-1.9344 0.71834,-0.58551 1.60294,-1.1585 2.689,-1.71704 1.33566,-0.68692 2.82604,-1.27591 4.41873,-1.81477 0.91309,-0.30893 1.90318,-0.61043 2.97552,-0.89949 2.75909,-0.74374 5.93213,-1.36936 9.45117,-1.81993 3.4485,-0.44153 7.02191,-0.68799 10.54487,-0.74159 0.11888,-0.002 0.23758,-0.003 0.35611,-0.005 3.42087,-0.0395 6.87572,0.10105 10.20745,0.42751 3.52675,0.34557 6.72,0.8799 9.50577,1.55294 1.13268,0.27365 2.17777,0.56526 3.14025,0.86932 1.5536,0.49082 3.01719,1.0374 4.3387,1.68661 1.10269,0.54172 2.00605,1.10621 2.74422,1.69041 1.03948,0.82267 1.92442,1.4682 2.24848,1.99812 -0.19311,-0.57592 -0.56732,-1.6128 -1.44388,-2.71822 -0.62575,-0.78911 -1.46647,-2.06647 -2.53663,-2.85214 -1.27528,-0.93627 -2.69843,-2.19648 -4.28815,-2.8662 -0.98885,-0.41658 -2.24548,-1.23914 -3.41727,-1.59811 -2.87657,-0.88121 -5.61102,-1.58246 -9.24402,-2.0046 -3.43498,-0.39913 -7.78163,-0.93367 -11.29835,-0.89735 -0.12203,0.001 -0.24424,0.003 -0.36663,0.005 -3.62086,0.0519 -8.29298,0.61115 -11.8461,1.12441 -3.62258,0.5233 -6.54204,1.50557 -9.38537,2.45693 -1.10746,0.37055 -2.4353,1.22472 -3.37108,1.64065 -1.62494,0.72224 -2.75555,1.66679 -4.03455,2.65099 -1.04655,0.80534 -1.65707,2.24134 -2.25723,3.03279 -0.84139,1.10955 -1.24261,2.1654 -1.41747,2.7334 z"
|
||||
id="path24"
|
||||
transform="translate(-546.58009,78.791545)"
|
||||
clip-path="url(#clipPath35)"
|
||||
sodipodi:nodetypes="csssssccssssscsssssccssssscc" />
|
||||
<path
|
||||
style="mix-blend-mode:normal;fill:#134f5a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.292;stroke-linecap:round;stroke-linejoin:round;filter:url(#filter60)"
|
||||
d="m 102.62873,226.09888 c 0.21837,0.59119 0.61147,1.63901 1.62645,2.90953 0.71917,0.90024 1.67343,1.81283 2.88011,2.66136 0.63885,0.44924 1.326,0.86437 2.04583,1.23422 1.73607,0.89199 3.88794,1.6902 6.44451,2.35832 3.15978,0.82576 6.73316,1.3998 10.52375,1.74639 2.77174,0.25344 5.543,0.37487 8.21345,0.40382 1.03453,0.0112 2.07172,0.008 3.10774,-0.0105 3.80268,-0.0675 7.55355,-0.33754 11.05616,-0.82447 3.61058,-0.50195 6.89851,-1.22612 9.69476,-2.17075 0.18263,-0.0617 0.36305,-0.1243 0.54123,-0.1878 2.50252,-0.89183 4.82056,-2.11237 6.51802,-3.52561 0.94359,-0.78561 1.6151,-1.56242 2.06492,-2.2946 0.63371,-1.03151 0.74175,-1.832 0.76927,-2.29791 -0.27946,0.40619 -0.77594,0.89664 -1.67451,1.38662 -0.63117,0.34418 -1.42595,0.67135 -2.41953,0.98728 -1.84319,0.58607 -3.98976,1.02107 -6.42445,1.47239 -0.16947,0.0314 -0.34092,0.0627 -0.51435,0.094 -2.66528,0.48001 -5.74678,0.9266 -9.18723,1.27047 -3.33185,0.33301 -6.89061,0.55814 -10.52391,0.6262 -0.98784,0.0185 -1.97654,0.0253 -2.96263,0.0197 -2.54936,-0.0146 -5.17908,-0.11853 -7.80849,-0.31818 -3.6162,-0.27458 -6.92286,-0.70577 -9.89494,-1.23993 -2.40547,-0.43232 -4.36461,-0.8899 -6.04479,-1.31771 -0.70208,-0.17876 -1.35634,-0.34389 -2.02406,-0.52251 -1.24448,-0.33289 -2.31304,-0.65483 -3.26912,-1.01993 -1.3453,-0.51373 -2.22471,-1.029 -2.73819,-1.44035 z"
|
||||
id="path59"
|
||||
clip-path="url(#clipPath34)" />
|
||||
</g>
|
||||
<path
|
||||
style="display:inline;mix-blend-mode:normal;fill:#3ba8bc;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:square;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="m 101.6,42.333334 c 16.93334,0 84.66667,50.800005 84.66667,93.133336 0,28.57209 -13.39181,41.07402 -25.11117,46.54433 -8.87807,4.14406 -11.33775,-1.382 -6.3999,-9.88879 3.31767,-5.71559 6.11107,-12.63293 6.11107,-19.72221 0,-12.7 -4.23333,-21.16666 -12.7,-29.63333 -8.46667,-8.46667 -19.34216,-12.70001 -25.4,-12.7 -8.46666,1e-5 -12.7,24.8856 -12.7,38.1 0,10.52973 3.14292,20.01182 6.30176,26.88277 2.97912,6.48004 0.48516,10.93086 -6.19604,8.47352 -7.29956,-2.68477 -16.052373,-7.98293 -21.272388,-18.42296 -8.466666,-16.93333 -4.233333,-41.3022 -4.233333,-63.5 0,-33.866665 -7e-6,-59.266666 16.933331,-59.266666 z"
|
||||
id="path3"
|
||||
inkscape:label="FLAME" />
|
||||
<path
|
||||
style="display:inline;mix-blend-mode:normal;fill:#3ba8bc;fill-opacity:0.151125;stroke:none;stroke-width:2.11667;stroke-linecap:square;stroke-opacity:1;paint-order:stroke fill markers;filter:url(#filter70)"
|
||||
d="m 101.6,42.333334 c 16.93334,0 84.66667,50.800005 84.66667,93.133336 0,28.57209 -13.39181,41.07402 -25.11117,46.54433 -8.87807,4.14406 -11.33775,-1.382 -6.3999,-9.88879 3.31767,-5.71559 6.11107,-12.63293 6.11107,-19.72221 0,-12.7 -4.23333,-21.16666 -12.7,-29.63333 -8.46667,-8.46667 -19.34216,-12.70001 -25.4,-12.7 -8.46666,1e-5 -12.7,24.8856 -12.7,38.1 0,10.52973 3.14292,20.01182 6.30176,26.88277 2.97912,6.48004 0.48516,10.93086 -6.19604,8.47352 -7.29956,-2.68477 -16.052373,-7.98293 -21.272388,-18.42296 -8.466666,-16.93333 -4.233333,-41.3022 -4.233333,-63.5 0,-33.866665 -7e-6,-59.266666 16.933331,-59.266666 z"
|
||||
id="path67"
|
||||
inkscape:label="GLOW" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,60 @@
|
||||
:root > * {
|
||||
--md-primary-fg-color: #1e97b5;
|
||||
--md-primary-fg-color--dark: color-mix(in srgb, var(--md-primary-fg-color), black 20%);
|
||||
--md-accent-fg-color: var(--md-primary-fg-color--dark);
|
||||
}
|
||||
|
||||
.md-logo > img {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root > * {
|
||||
--md-primary-fg-color: #167188;
|
||||
--md-accent-fg-color: #1e97b5;
|
||||
--md-typeset-a-color: color-mix(in srgb, var(--md-accent-fg-color), white 30%);
|
||||
}
|
||||
|
||||
.md-logo > img {
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.md-nav--primary .md-nav__title {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-nav__title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-main {
|
||||
min-height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.md-nav__link--active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .gp, .highlight .go {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.md-source-file__fact {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.85em;
|
||||
box-shadow: 0 0 0 1px inset var(--md-accent-fg-color--transparent);
|
||||
padding: 0.2rem 0.3rem;
|
||||
}
|
||||
|
||||
.md-typeset table:not([class]) td {
|
||||
padding: .5em 1.25em;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Generated
+6
-22
@@ -1,27 +1,12 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1731533336,
|
||||
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1742707865,
|
||||
"narHash": "sha256-RVQQZy38O3Zb8yoRJhuFgWo/iDIDj0hEdRTVfhOtzRk=",
|
||||
"lastModified": 1752077645,
|
||||
"narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dd613136ee91f67e5dba3f3f41ac99ae89c5406b",
|
||||
"rev": "be9e214982e20b8310878ac2baa063a961c1bdf6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -33,7 +18,6 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
@@ -45,11 +29,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742697269,
|
||||
"narHash": "sha256-Lpp0XyAtIl1oGJzNmTiTGLhTkcUjwSkEb0gOiNzYFGM=",
|
||||
"lastModified": 1752374969,
|
||||
"narHash": "sha256-Ky3ynEkJXih7mvWyt9DWoiSiZGqPeHLU1tlBU4b0mcc=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "01973c84732f9275c50c5f075dd1f54cc04b3316",
|
||||
"rev": "75fb000638e6d0f57cb1e8b7a4550cbdd8c76f1d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
|
||||
# NOTE: This is not necessary for end users
|
||||
# You can omit it with `inputs.rust-overlay.follows = ""`
|
||||
@@ -18,7 +17,6 @@
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
nix-filter,
|
||||
rust-overlay,
|
||||
}:
|
||||
let
|
||||
@@ -50,16 +48,16 @@
|
||||
pname = "niri";
|
||||
version = self.shortRev or self.dirtyShortRev or "unknown";
|
||||
|
||||
src = nix-filter.lib.filter {
|
||||
root = self;
|
||||
include = [
|
||||
"niri-config"
|
||||
"niri-ipc"
|
||||
"niri-visual-tests"
|
||||
"resources"
|
||||
"src"
|
||||
./Cargo.lock
|
||||
src = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.unions [
|
||||
./niri-config
|
||||
./niri-ipc
|
||||
./niri-visual-tests
|
||||
./resources
|
||||
./src
|
||||
./Cargo.toml
|
||||
./Cargo.lock
|
||||
];
|
||||
};
|
||||
|
||||
@@ -117,6 +115,12 @@
|
||||
export XDG_RUNTIME_DIR="$(mktemp -d)"
|
||||
'';
|
||||
|
||||
checkFlags = [
|
||||
# These tests require the ability to access a "valid EGL Display", but that won't work
|
||||
# inside the Nix sandbox
|
||||
"--skip=tests::animations"
|
||||
];
|
||||
|
||||
postInstall =
|
||||
''
|
||||
installShellCompletion --cmd niri \
|
||||
|
||||
@@ -9,7 +9,7 @@ repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
csscolorparser = "0.7.0"
|
||||
csscolorparser = "0.7.2"
|
||||
knuffel = "3.2.0"
|
||||
miette = { version = "5.10.0", features = ["fancy-no-backtrace"] }
|
||||
niri-ipc = { version = "25.5.1", path = "../niri-ipc" }
|
||||
|
||||
+511
-35
@@ -3,6 +3,8 @@ extern crate tracing;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::ops::{Mul, MulAssign};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
@@ -39,6 +41,8 @@ pub struct Config {
|
||||
pub outputs: Outputs,
|
||||
#[knuffel(children(name = "spawn-at-startup"))]
|
||||
pub spawn_at_startup: Vec<SpawnAtStartup>,
|
||||
#[knuffel(children(name = "spawn-sh-at-startup"))]
|
||||
pub spawn_sh_at_startup: Vec<SpawnShAtStartup>,
|
||||
#[knuffel(child, default)]
|
||||
pub layout: Layout,
|
||||
#[knuffel(child, default)]
|
||||
@@ -58,6 +62,8 @@ pub struct Config {
|
||||
#[knuffel(child, default)]
|
||||
pub hotkey_overlay: HotkeyOverlay,
|
||||
#[knuffel(child, default)]
|
||||
pub config_notification: ConfigNotification,
|
||||
#[knuffel(child, default)]
|
||||
pub animations: Animations,
|
||||
#[knuffel(child, default)]
|
||||
pub gestures: Gestures,
|
||||
@@ -65,6 +71,8 @@ pub struct Config {
|
||||
pub overview: Overview,
|
||||
#[knuffel(child, default)]
|
||||
pub environment: Environment,
|
||||
#[knuffel(child, default)]
|
||||
pub xwayland_satellite: XwaylandSatellite,
|
||||
#[knuffel(children(name = "window-rule"))]
|
||||
pub window_rules: Vec<WindowRule>,
|
||||
#[knuffel(children(name = "layer-rule"))]
|
||||
@@ -153,7 +161,7 @@ pub struct Xkb {
|
||||
}
|
||||
|
||||
impl Xkb {
|
||||
pub fn to_xkb_config(&self) -> XkbConfig {
|
||||
pub fn to_xkb_config(&self) -> XkbConfig<'_> {
|
||||
XkbConfig {
|
||||
rules: &self.rules,
|
||||
model: &self.model,
|
||||
@@ -185,6 +193,25 @@ pub enum TrackLayout {
|
||||
Window,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct ScrollFactor {
|
||||
#[knuffel(argument)]
|
||||
pub base: Option<FloatOrInt<0, 100>>,
|
||||
#[knuffel(property)]
|
||||
pub horizontal: Option<FloatOrInt<-100, 100>>,
|
||||
#[knuffel(property)]
|
||||
pub vertical: Option<FloatOrInt<-100, 100>>,
|
||||
}
|
||||
|
||||
impl ScrollFactor {
|
||||
pub fn h_v_factors(&self) -> (f64, f64) {
|
||||
let base_value = self.base.map(|f| f.0).unwrap_or(1.0);
|
||||
let h = self.horizontal.map(|f| f.0).unwrap_or(base_value);
|
||||
let v = self.vertical.map(|f| f.0).unwrap_or(base_value);
|
||||
(h, v)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
pub struct Touchpad {
|
||||
#[knuffel(child)]
|
||||
@@ -204,13 +231,15 @@ pub struct Touchpad {
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub click_method: Option<ClickMethod>,
|
||||
#[knuffel(child, unwrap(argument), default)]
|
||||
pub accel_speed: f64,
|
||||
pub accel_speed: FloatOrInt<-1, 1>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub accel_profile: Option<AccelProfile>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub scroll_method: Option<ScrollMethod>,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_button: Option<u32>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_button_lock: bool,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub tap_button_map: Option<TapButtonMap>,
|
||||
#[knuffel(child)]
|
||||
@@ -219,8 +248,8 @@ pub struct Touchpad {
|
||||
pub disabled_on_external_mouse: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_factor: Option<FloatOrInt<0, 100>>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_factor: Option<ScrollFactor>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
@@ -230,7 +259,7 @@ pub struct Mouse {
|
||||
#[knuffel(child)]
|
||||
pub natural_scroll: bool,
|
||||
#[knuffel(child, unwrap(argument), default)]
|
||||
pub accel_speed: f64,
|
||||
pub accel_speed: FloatOrInt<-1, 1>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub accel_profile: Option<AccelProfile>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
@@ -238,11 +267,13 @@ pub struct Mouse {
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_button: Option<u32>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_button_lock: bool,
|
||||
#[knuffel(child)]
|
||||
pub left_handed: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_factor: Option<FloatOrInt<0, 100>>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_factor: Option<ScrollFactor>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
@@ -252,7 +283,7 @@ pub struct Trackpoint {
|
||||
#[knuffel(child)]
|
||||
pub natural_scroll: bool,
|
||||
#[knuffel(child, unwrap(argument), default)]
|
||||
pub accel_speed: f64,
|
||||
pub accel_speed: FloatOrInt<-1, 1>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub accel_profile: Option<AccelProfile>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
@@ -260,6 +291,8 @@ pub struct Trackpoint {
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_button: Option<u32>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_button_lock: bool,
|
||||
#[knuffel(child)]
|
||||
pub left_handed: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
@@ -272,7 +305,7 @@ pub struct Trackball {
|
||||
#[knuffel(child)]
|
||||
pub natural_scroll: bool,
|
||||
#[knuffel(child, unwrap(argument), default)]
|
||||
pub accel_speed: f64,
|
||||
pub accel_speed: FloatOrInt<-1, 1>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
pub accel_profile: Option<AccelProfile>,
|
||||
#[knuffel(child, unwrap(argument, str))]
|
||||
@@ -280,6 +313,8 @@ pub struct Trackball {
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub scroll_button: Option<u32>,
|
||||
#[knuffel(child)]
|
||||
pub scroll_button_lock: bool,
|
||||
#[knuffel(child)]
|
||||
pub left_handed: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
@@ -573,6 +608,12 @@ pub struct SpawnAtStartup {
|
||||
pub command: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SpawnShAtStartup {
|
||||
#[knuffel(argument)]
|
||||
pub command: String,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
||||
pub struct FocusRing {
|
||||
#[knuffel(child)]
|
||||
@@ -1031,6 +1072,14 @@ pub struct Struts {
|
||||
pub struct HotkeyOverlay {
|
||||
#[knuffel(child)]
|
||||
pub skip_at_startup: bool,
|
||||
#[knuffel(child)]
|
||||
pub hide_not_bound: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ConfigNotification {
|
||||
#[knuffel(child)]
|
||||
pub disable_failed: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -1043,8 +1092,8 @@ pub struct Clipboard {
|
||||
pub struct Animations {
|
||||
#[knuffel(child)]
|
||||
pub off: bool,
|
||||
#[knuffel(child, unwrap(argument), default = 1.)]
|
||||
pub slowdown: f64,
|
||||
#[knuffel(child, unwrap(argument), default = FloatOrInt(1.))]
|
||||
pub slowdown: FloatOrInt<0, { i32::MAX }>,
|
||||
#[knuffel(child, default)]
|
||||
pub workspace_switch: WorkspaceSwitchAnim,
|
||||
#[knuffel(child, default)]
|
||||
@@ -1069,7 +1118,7 @@ impl Default for Animations {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
off: false,
|
||||
slowdown: 1.,
|
||||
slowdown: FloatOrInt(1.),
|
||||
workspace_switch: Default::default(),
|
||||
horizontal_view_movement: Default::default(),
|
||||
window_movement: Default::default(),
|
||||
@@ -1362,6 +1411,23 @@ pub struct EnvironmentVariable {
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct XwaylandSatellite {
|
||||
#[knuffel(child)]
|
||||
pub off: bool,
|
||||
#[knuffel(child, unwrap(argument), default = Self::default().path)]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl Default for XwaylandSatellite {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
off: false,
|
||||
path: String::from("xwayland-satellite"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Workspace {
|
||||
#[knuffel(argument)]
|
||||
@@ -1661,6 +1727,7 @@ pub enum Action {
|
||||
DebugToggleOpaqueRegions,
|
||||
DebugToggleDamage,
|
||||
Spawn(#[knuffel(arguments)] Vec<String>),
|
||||
SpawnSh(#[knuffel(argument)] String),
|
||||
DoScreenTransition(#[knuffel(property(name = "delay-ms"))] Option<u16>),
|
||||
#[knuffel(skip)]
|
||||
ConfirmScreenshot {
|
||||
@@ -1893,6 +1960,8 @@ pub enum Action {
|
||||
SetWindowUrgent(u64),
|
||||
#[knuffel(skip)]
|
||||
UnsetWindowUrgent(u64),
|
||||
#[knuffel(skip)]
|
||||
LoadConfigFile,
|
||||
}
|
||||
|
||||
impl From<niri_ipc::Action> for Action {
|
||||
@@ -1902,6 +1971,7 @@ impl From<niri_ipc::Action> for Action {
|
||||
niri_ipc::Action::PowerOffMonitors {} => Self::PowerOffMonitors,
|
||||
niri_ipc::Action::PowerOnMonitors {} => Self::PowerOnMonitors,
|
||||
niri_ipc::Action::Spawn { command } => Self::Spawn(command),
|
||||
niri_ipc::Action::SpawnSh { command } => Self::SpawnSh(command),
|
||||
niri_ipc::Action::DoScreenTransition { delay_ms } => Self::DoScreenTransition(delay_ms),
|
||||
niri_ipc::Action::Screenshot { show_pointer } => Self::Screenshot(show_pointer),
|
||||
niri_ipc::Action::ScreenshotScreen {
|
||||
@@ -2168,6 +2238,7 @@ impl From<niri_ipc::Action> for Action {
|
||||
niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id),
|
||||
niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id),
|
||||
niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id),
|
||||
niri_ipc::Action::LoadConfigFile {} => Self::LoadConfigFile,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2302,14 +2373,14 @@ pub struct DebugConfig {
|
||||
#[knuffel(child)]
|
||||
pub wait_for_frame_completion_before_queueing: bool,
|
||||
#[knuffel(child)]
|
||||
pub wait_for_frame_completion_in_pipewire: bool,
|
||||
#[knuffel(child)]
|
||||
pub enable_overlay_planes: bool,
|
||||
#[knuffel(child)]
|
||||
pub disable_cursor_plane: bool,
|
||||
#[knuffel(child)]
|
||||
pub disable_direct_scanout: bool,
|
||||
#[knuffel(child)]
|
||||
pub keep_max_bpc_unchanged: bool,
|
||||
#[knuffel(child)]
|
||||
pub restrict_primary_scanout_to_matching_format: bool,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub render_drm_device: Option<PathBuf>,
|
||||
@@ -2329,6 +2400,10 @@ pub struct DebugConfig {
|
||||
pub strict_new_window_focus_policy: bool,
|
||||
#[knuffel(child)]
|
||||
pub honor_xdg_activation_with_invalid_serial: bool,
|
||||
#[knuffel(child)]
|
||||
pub deactivate_unfocused_windows: bool,
|
||||
#[knuffel(child)]
|
||||
pub skip_cursor_only_updates_during_vrr: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -2337,14 +2412,118 @@ pub enum PreviewRender {
|
||||
ScreenCapture,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: &Path) -> miette::Result<Self> {
|
||||
let _span = tracy_client::span!("Config::load");
|
||||
Self::load_internal(path).context("error loading config")
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConfigPath {
|
||||
/// Explicitly set config path.
|
||||
///
|
||||
/// Load the config only from this path, never create it.
|
||||
Explicit(PathBuf),
|
||||
|
||||
/// Default config path.
|
||||
///
|
||||
/// Prioritize the user path, fallback to the system path, fallback to creating the user path
|
||||
/// at compositor startup.
|
||||
Regular {
|
||||
/// User config path, usually `$XDG_CONFIG_HOME/niri/config.kdl`.
|
||||
user_path: PathBuf,
|
||||
/// System config path, usually `/etc/niri/config.kdl`.
|
||||
system_path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl ConfigPath {
|
||||
/// Load the config, or return an error if it doesn't exist.
|
||||
pub fn load(&self) -> miette::Result<Config> {
|
||||
let _span = tracy_client::span!("ConfigPath::load");
|
||||
|
||||
self.load_inner(|user_path, system_path| {
|
||||
Err(miette::miette!(
|
||||
"no config file found; create one at {user_path:?} or {system_path:?}",
|
||||
))
|
||||
})
|
||||
.context("error loading config")
|
||||
}
|
||||
|
||||
fn load_internal(path: &Path) -> miette::Result<Self> {
|
||||
let contents = std::fs::read_to_string(path)
|
||||
/// Load the config, or create it if it doesn't exist.
|
||||
///
|
||||
/// Returns a tuple containing the path that was created, if any, and the loaded config.
|
||||
///
|
||||
/// If the config was created, but for some reason could not be read afterwards,
|
||||
/// this may return `(Some(_), Err(_))`.
|
||||
pub fn load_or_create(&self) -> (Option<&Path>, miette::Result<Config>) {
|
||||
let _span = tracy_client::span!("ConfigPath::load_or_create");
|
||||
|
||||
let mut created_at = None;
|
||||
|
||||
let result = self
|
||||
.load_inner(|user_path, _| {
|
||||
Self::create(user_path, &mut created_at)
|
||||
.map(|()| user_path)
|
||||
.with_context(|| format!("error creating config at {user_path:?}"))
|
||||
})
|
||||
.context("error loading config");
|
||||
|
||||
(created_at, result)
|
||||
}
|
||||
|
||||
fn load_inner<'a>(
|
||||
&'a self,
|
||||
maybe_create: impl FnOnce(&'a Path, &'a Path) -> miette::Result<&'a Path>,
|
||||
) -> miette::Result<Config> {
|
||||
let path = match self {
|
||||
ConfigPath::Explicit(path) => path.as_path(),
|
||||
ConfigPath::Regular {
|
||||
user_path,
|
||||
system_path,
|
||||
} => {
|
||||
if user_path.exists() {
|
||||
user_path.as_path()
|
||||
} else if system_path.exists() {
|
||||
system_path.as_path()
|
||||
} else {
|
||||
maybe_create(user_path.as_path(), system_path.as_path())?
|
||||
}
|
||||
}
|
||||
};
|
||||
Config::load(path)
|
||||
}
|
||||
|
||||
fn create<'a>(path: &'a Path, created_at: &mut Option<&'a Path>) -> miette::Result<()> {
|
||||
if let Some(default_parent) = path.parent() {
|
||||
fs::create_dir_all(default_parent)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("error creating config directory {default_parent:?}"))?;
|
||||
}
|
||||
|
||||
// Create the config and fill it with the default config if it doesn't exist.
|
||||
let mut new_file = match File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(path)
|
||||
{
|
||||
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => return Ok(()),
|
||||
res => res,
|
||||
}
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("error opening config file at {path:?}"))?;
|
||||
|
||||
*created_at = Some(path);
|
||||
|
||||
let default = include_bytes!("../../resources/default-config.kdl");
|
||||
|
||||
new_file
|
||||
.write_all(default)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("error writing default config to {path:?}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: &Path) -> miette::Result<Self> {
|
||||
let contents = fs::read_to_string(path)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("error reading {path:?}"))?;
|
||||
|
||||
@@ -2652,7 +2831,10 @@ impl FromStr for Color {
|
||||
type Err = miette::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let color = csscolorparser::parse(s).into_diagnostic()?.to_array();
|
||||
let color = csscolorparser::parse(s)
|
||||
.into_diagnostic()?
|
||||
.clamp()
|
||||
.to_array();
|
||||
Ok(Self::from_array_unpremul(color))
|
||||
}
|
||||
}
|
||||
@@ -3043,7 +3225,7 @@ impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
val,
|
||||
"named workspace",
|
||||
format!("duplicate named workspace: {}", s),
|
||||
format!("duplicate named workspace: {s}"),
|
||||
));
|
||||
return Ok(Self(String::new()));
|
||||
}
|
||||
@@ -3682,7 +3864,7 @@ where
|
||||
}
|
||||
match Action::decode_node(child, ctx) {
|
||||
Ok(action) => {
|
||||
if !matches!(action, Action::Spawn(_)) {
|
||||
if !matches!(action, Action::Spawn(_) | Action::SpawnSh(_)) {
|
||||
if let Some(node) = allow_when_locked_node {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
node,
|
||||
@@ -3901,6 +4083,237 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scroll_factor_combined() {
|
||||
// Test combined scroll-factor syntax
|
||||
let parsed = do_parse(
|
||||
r#"
|
||||
input {
|
||||
mouse {
|
||||
scroll-factor 2.0
|
||||
}
|
||||
touchpad {
|
||||
scroll-factor 1.5
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
2.0,
|
||||
),
|
||||
),
|
||||
horizontal: None,
|
||||
vertical: None,
|
||||
},
|
||||
)
|
||||
"#);
|
||||
assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
1.5,
|
||||
),
|
||||
),
|
||||
horizontal: None,
|
||||
vertical: None,
|
||||
},
|
||||
)
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scroll_factor_split() {
|
||||
// Test split horizontal/vertical syntax
|
||||
let parsed = do_parse(
|
||||
r#"
|
||||
input {
|
||||
mouse {
|
||||
scroll-factor horizontal=2.0 vertical=-1.0
|
||||
}
|
||||
touchpad {
|
||||
scroll-factor horizontal=-1.5 vertical=0.5
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: None,
|
||||
horizontal: Some(
|
||||
FloatOrInt(
|
||||
2.0,
|
||||
),
|
||||
),
|
||||
vertical: Some(
|
||||
FloatOrInt(
|
||||
-1.0,
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
"#);
|
||||
assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: None,
|
||||
horizontal: Some(
|
||||
FloatOrInt(
|
||||
-1.5,
|
||||
),
|
||||
),
|
||||
vertical: Some(
|
||||
FloatOrInt(
|
||||
0.5,
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scroll_factor_partial() {
|
||||
// Test partial specification (only one axis)
|
||||
let parsed = do_parse(
|
||||
r#"
|
||||
input {
|
||||
mouse {
|
||||
scroll-factor horizontal=2.0
|
||||
}
|
||||
touchpad {
|
||||
scroll-factor vertical=-1.5
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: None,
|
||||
horizontal: Some(
|
||||
FloatOrInt(
|
||||
2.0,
|
||||
),
|
||||
),
|
||||
vertical: None,
|
||||
},
|
||||
)
|
||||
"#);
|
||||
assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: None,
|
||||
horizontal: None,
|
||||
vertical: Some(
|
||||
FloatOrInt(
|
||||
-1.5,
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scroll_factor_mixed() {
|
||||
// Test mixed base + override syntax
|
||||
let parsed = do_parse(
|
||||
r#"
|
||||
input {
|
||||
mouse {
|
||||
scroll-factor 2 vertical=-1
|
||||
}
|
||||
touchpad {
|
||||
scroll-factor 1.5 horizontal=3
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
2.0,
|
||||
),
|
||||
),
|
||||
horizontal: None,
|
||||
vertical: Some(
|
||||
FloatOrInt(
|
||||
-1.0,
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
"#);
|
||||
assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#"
|
||||
Some(
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
1.5,
|
||||
),
|
||||
),
|
||||
horizontal: Some(
|
||||
FloatOrInt(
|
||||
3.0,
|
||||
),
|
||||
),
|
||||
vertical: None,
|
||||
},
|
||||
)
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scroll_factor_h_v_factors() {
|
||||
let sf = ScrollFactor {
|
||||
base: Some(FloatOrInt(2.0)),
|
||||
horizontal: None,
|
||||
vertical: None,
|
||||
};
|
||||
assert_debug_snapshot!(sf.h_v_factors(), @r#"
|
||||
(
|
||||
2.0,
|
||||
2.0,
|
||||
)
|
||||
"#);
|
||||
|
||||
let sf = ScrollFactor {
|
||||
base: None,
|
||||
horizontal: Some(FloatOrInt(3.0)),
|
||||
vertical: Some(FloatOrInt(-1.0)),
|
||||
};
|
||||
assert_debug_snapshot!(sf.h_v_factors(), @r#"
|
||||
(
|
||||
3.0,
|
||||
-1.0,
|
||||
)
|
||||
"#);
|
||||
|
||||
let sf = ScrollFactor {
|
||||
base: Some(FloatOrInt(2.0)),
|
||||
horizontal: Some(FloatOrInt(1.0)),
|
||||
vertical: None,
|
||||
};
|
||||
assert_debug_snapshot!(sf.h_v_factors(), @r"
|
||||
(
|
||||
1.0,
|
||||
2.0,
|
||||
)
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
let parsed = do_parse(
|
||||
@@ -3926,6 +4339,7 @@ mod tests {
|
||||
accel-profile "flat"
|
||||
scroll-method "two-finger"
|
||||
scroll-button 272
|
||||
scroll-button-lock
|
||||
tap-button-map "left-middle-right"
|
||||
disabled-on-external-mouse
|
||||
scroll-factor 0.9
|
||||
@@ -3957,6 +4371,7 @@ mod tests {
|
||||
accel-profile "flat"
|
||||
scroll-method "edge"
|
||||
scroll-button 275
|
||||
scroll-button-lock
|
||||
left-handed
|
||||
middle-emulation
|
||||
}
|
||||
@@ -4048,6 +4463,7 @@ mod tests {
|
||||
}
|
||||
|
||||
spawn-at-startup "alacritty" "-e" "fish"
|
||||
spawn-sh-at-startup "qs -c ~/source/qs/MyAwesomeShell"
|
||||
|
||||
prefer-no-csd
|
||||
|
||||
@@ -4144,6 +4560,7 @@ mod tests {
|
||||
Mod+Shift+1 { focus-workspace "workspace-1"; }
|
||||
Mod+Shift+E allow-inhibiting=false { quit skip-confirmation=true; }
|
||||
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
|
||||
Super+Alt+S allow-when-locked=true { spawn-sh "pkill orca || exec orca"; }
|
||||
}
|
||||
|
||||
switch-events {
|
||||
@@ -4195,7 +4612,9 @@ mod tests {
|
||||
click_method: Some(
|
||||
Clickfinger,
|
||||
),
|
||||
accel_speed: 0.2,
|
||||
accel_speed: FloatOrInt(
|
||||
0.2,
|
||||
),
|
||||
accel_profile: Some(
|
||||
Flat,
|
||||
),
|
||||
@@ -4205,6 +4624,7 @@ mod tests {
|
||||
scroll_button: Some(
|
||||
272,
|
||||
),
|
||||
scroll_button_lock: true,
|
||||
tap_button_map: Some(
|
||||
LeftMiddleRight,
|
||||
),
|
||||
@@ -4212,15 +4632,23 @@ mod tests {
|
||||
disabled_on_external_mouse: true,
|
||||
middle_emulation: false,
|
||||
scroll_factor: Some(
|
||||
FloatOrInt(
|
||||
0.9,
|
||||
),
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
0.9,
|
||||
),
|
||||
),
|
||||
horizontal: None,
|
||||
vertical: None,
|
||||
},
|
||||
),
|
||||
},
|
||||
mouse: Mouse {
|
||||
off: false,
|
||||
natural_scroll: true,
|
||||
accel_speed: 0.4,
|
||||
accel_speed: FloatOrInt(
|
||||
0.4,
|
||||
),
|
||||
accel_profile: Some(
|
||||
Flat,
|
||||
),
|
||||
@@ -4230,18 +4658,27 @@ mod tests {
|
||||
scroll_button: Some(
|
||||
273,
|
||||
),
|
||||
scroll_button_lock: false,
|
||||
left_handed: false,
|
||||
middle_emulation: true,
|
||||
scroll_factor: Some(
|
||||
FloatOrInt(
|
||||
0.2,
|
||||
),
|
||||
ScrollFactor {
|
||||
base: Some(
|
||||
FloatOrInt(
|
||||
0.2,
|
||||
),
|
||||
),
|
||||
horizontal: None,
|
||||
vertical: None,
|
||||
},
|
||||
),
|
||||
},
|
||||
trackpoint: Trackpoint {
|
||||
off: true,
|
||||
natural_scroll: true,
|
||||
accel_speed: 0.0,
|
||||
accel_speed: FloatOrInt(
|
||||
0.0,
|
||||
),
|
||||
accel_profile: Some(
|
||||
Flat,
|
||||
),
|
||||
@@ -4251,13 +4688,16 @@ mod tests {
|
||||
scroll_button: Some(
|
||||
274,
|
||||
),
|
||||
scroll_button_lock: false,
|
||||
left_handed: false,
|
||||
middle_emulation: false,
|
||||
},
|
||||
trackball: Trackball {
|
||||
off: true,
|
||||
natural_scroll: true,
|
||||
accel_speed: 0.0,
|
||||
accel_speed: FloatOrInt(
|
||||
0.0,
|
||||
),
|
||||
accel_profile: Some(
|
||||
Flat,
|
||||
),
|
||||
@@ -4267,6 +4707,7 @@ mod tests {
|
||||
scroll_button: Some(
|
||||
275,
|
||||
),
|
||||
scroll_button_lock: true,
|
||||
left_handed: true,
|
||||
middle_emulation: true,
|
||||
},
|
||||
@@ -4365,6 +4806,11 @@ mod tests {
|
||||
],
|
||||
},
|
||||
],
|
||||
spawn_sh_at_startup: [
|
||||
SpawnShAtStartup {
|
||||
command: "qs -c ~/source/qs/MyAwesomeShell",
|
||||
},
|
||||
],
|
||||
layout: Layout {
|
||||
focus_ring: FocusRing {
|
||||
off: false,
|
||||
@@ -4608,10 +5054,16 @@ mod tests {
|
||||
},
|
||||
hotkey_overlay: HotkeyOverlay {
|
||||
skip_at_startup: true,
|
||||
hide_not_bound: false,
|
||||
},
|
||||
config_notification: ConfigNotification {
|
||||
disable_failed: false,
|
||||
},
|
||||
animations: Animations {
|
||||
off: false,
|
||||
slowdown: 2.0,
|
||||
slowdown: FloatOrInt(
|
||||
2.0,
|
||||
),
|
||||
workspace_switch: WorkspaceSwitchAnim(
|
||||
Animation {
|
||||
off: false,
|
||||
@@ -4791,6 +5243,10 @@ mod tests {
|
||||
},
|
||||
],
|
||||
),
|
||||
xwayland_satellite: XwaylandSatellite {
|
||||
off: false,
|
||||
path: "xwayland-satellite",
|
||||
},
|
||||
window_rules: [
|
||||
WindowRule {
|
||||
matches: [
|
||||
@@ -5242,6 +5698,24 @@ mod tests {
|
||||
allow_inhibiting: true,
|
||||
hotkey_overlay_title: None,
|
||||
},
|
||||
Bind {
|
||||
key: Key {
|
||||
trigger: Keysym(
|
||||
XK_s,
|
||||
),
|
||||
modifiers: Modifiers(
|
||||
ALT | SUPER,
|
||||
),
|
||||
},
|
||||
action: SpawnSh(
|
||||
"pkill orca || exec orca",
|
||||
),
|
||||
repeat: true,
|
||||
cooldown: None,
|
||||
allow_when_locked: true,
|
||||
allow_inhibiting: true,
|
||||
hotkey_overlay_title: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
switch_events: SwitchBinds {
|
||||
@@ -5270,10 +5744,10 @@ mod tests {
|
||||
preview_render: None,
|
||||
dbus_interfaces_in_non_session_instances: false,
|
||||
wait_for_frame_completion_before_queueing: false,
|
||||
wait_for_frame_completion_in_pipewire: false,
|
||||
enable_overlay_planes: false,
|
||||
disable_cursor_plane: false,
|
||||
disable_direct_scanout: false,
|
||||
keep_max_bpc_unchanged: false,
|
||||
restrict_primary_scanout_to_matching_format: false,
|
||||
render_drm_device: Some(
|
||||
"/dev/dri/renderD129",
|
||||
@@ -5286,6 +5760,8 @@ mod tests {
|
||||
disable_monitor_names: false,
|
||||
strict_new_window_focus_policy: false,
|
||||
honor_xdg_activation_with_invalid_serial: false,
|
||||
deactivate_unfocused_windows: false,
|
||||
skip_cursor_only_updates_during_vrr: false,
|
||||
},
|
||||
workspaces: [
|
||||
Workspace {
|
||||
|
||||
@@ -56,7 +56,7 @@ fn extract_kdl_from_file(file_contents: &str, filename: &str) -> Vec<KdlCodeBloc
|
||||
|
||||
#[test]
|
||||
fn wiki_docs_parses() {
|
||||
let wiki_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../wiki");
|
||||
let wiki_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../docs/wiki");
|
||||
|
||||
let code_blocks = fs::read_dir(wiki_dir)
|
||||
.unwrap()
|
||||
@@ -95,8 +95,7 @@ fn wiki_docs_parses() {
|
||||
}
|
||||
} else if must_fail {
|
||||
errors.push(format!(
|
||||
"Expected error parsing wiki KDL code block at {}:{}",
|
||||
filename, line_number
|
||||
"Expected error parsing wiki KDL code block at {filename}:{line_number}",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, optional = true }
|
||||
schemars = { version = "0.8.22", optional = true }
|
||||
schemars = { version = "1.0.4", optional = true }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
|
||||
+75
-1
@@ -203,6 +203,12 @@ pub enum Action {
|
||||
#[cfg_attr(feature = "clap", arg(last = true, required = true))]
|
||||
command: Vec<String>,
|
||||
},
|
||||
/// Spawn a command through the shell.
|
||||
SpawnSh {
|
||||
/// Command to run.
|
||||
#[cfg_attr(feature = "clap", arg(last = true, required = true))]
|
||||
command: String,
|
||||
},
|
||||
/// Do a screen transition.
|
||||
DoScreenTransition {
|
||||
/// Delay in milliseconds for the screen to freeze before starting the transition.
|
||||
@@ -332,7 +338,7 @@ pub enum Action {
|
||||
FocusWindowUpOrColumnLeft {},
|
||||
/// Focus the window above or the column to the right.
|
||||
FocusWindowUpOrColumnRight {},
|
||||
/// Focus the window or the workspace above.
|
||||
/// Focus the window or the workspace below.
|
||||
FocusWindowOrWorkspaceDown {},
|
||||
/// Focus the window or the workspace above.
|
||||
FocusWindowOrWorkspaceUp {},
|
||||
@@ -840,6 +846,11 @@ pub enum Action {
|
||||
#[cfg_attr(feature = "clap", arg(long))]
|
||||
id: u64,
|
||||
},
|
||||
/// Reload the config file.
|
||||
///
|
||||
/// Can be useful for scripts changing the config file, to avoid waiting the small duration for
|
||||
/// niri's config file watcher to notice the changes.
|
||||
LoadConfigFile {},
|
||||
}
|
||||
|
||||
/// Change in window or column size.
|
||||
@@ -1150,6 +1161,54 @@ pub struct Window {
|
||||
pub is_floating: bool,
|
||||
/// Whether this window requests your attention.
|
||||
pub is_urgent: bool,
|
||||
/// Position- and size-related properties of the window.
|
||||
pub layout: WindowLayout,
|
||||
}
|
||||
|
||||
/// Position- and size-related properties of a [`Window`].
|
||||
///
|
||||
/// Optional properties will be unset for some windows, do not rely on them being present. Whether
|
||||
/// some optional properties are present or absent for certain window types may change across niri
|
||||
/// releases.
|
||||
///
|
||||
/// All sizes and positions are in *logical pixels* unless stated otherwise. Logical sizes may be
|
||||
/// fractional. For example, at 1.25 monitor scale, a 2-physical-pixel-wide window border is 1.6
|
||||
/// logical pixels wide.
|
||||
///
|
||||
/// This struct contains positions and sizes both for full tiles ([`Self::tile_size`],
|
||||
/// [`Self::tile_pos_in_workspace_view`]) and the window geometry ([`Self::window_size`],
|
||||
/// [`Self::window_offset_in_tile`]). For visual displays, use the tile properties, as they
|
||||
/// correspond to what the user visually considers "window". The window properties on the other
|
||||
/// hand are mainly useful when you need to know the underlying Wayland window sizes, e.g. for
|
||||
/// application debugging.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
|
||||
pub struct WindowLayout {
|
||||
/// Location of a tiled window within a workspace: (column index, tile index in column).
|
||||
///
|
||||
/// The indices are 1-based, i.e. the leftmost column is at index 1 and the topmost tile in a
|
||||
/// column is at index 1. This is consistent with [`Action::FocusColumn`] and
|
||||
/// [`Action::FocusWindowInColumn`].
|
||||
pub pos_in_scrolling_layout: Option<(usize, usize)>,
|
||||
/// Size of the tile this window is in, including decorations like borders.
|
||||
pub tile_size: (f64, f64),
|
||||
/// Size of the window's visual geometry itself.
|
||||
///
|
||||
/// Does not include niri decorations like borders.
|
||||
///
|
||||
/// Currently, Wayland toplevel windows can only be integer-sized in logical pixels, even
|
||||
/// though it doesn't necessarily align to physical pixels.
|
||||
pub window_size: (i32, i32),
|
||||
/// Tile position within the current view of the workspace.
|
||||
///
|
||||
/// This is the same "workspace view" as in gradients' `relative-to` in the niri config.
|
||||
pub tile_pos_in_workspace_view: Option<(f64, f64)>,
|
||||
/// Location of the window's visual geometry within its tile.
|
||||
///
|
||||
/// This includes things like border sizes. For fullscreened fixed-size windows this includes
|
||||
/// the distance from the corner of the black backdrop to the corner of the (centered) window
|
||||
/// contents.
|
||||
pub window_offset_in_tile: (f64, f64),
|
||||
}
|
||||
|
||||
/// Output configuration change result.
|
||||
@@ -1326,6 +1385,11 @@ pub enum Event {
|
||||
/// The new urgency state of the window.
|
||||
urgent: bool,
|
||||
},
|
||||
/// The layout of one or more windows has changed.
|
||||
WindowLayoutsChanged {
|
||||
/// Pairs consisting of a window id and new layout information for the window.
|
||||
changes: Vec<(u64, WindowLayout)>,
|
||||
},
|
||||
/// The configured keyboard layouts have changed.
|
||||
KeyboardLayoutsChanged {
|
||||
/// The new keyboard layout configuration.
|
||||
@@ -1341,6 +1405,16 @@ pub enum Event {
|
||||
/// The new state of the overview.
|
||||
is_open: bool,
|
||||
},
|
||||
/// The configuration was reloaded.
|
||||
///
|
||||
/// You will always receive this event when connecting to the event stream, indicating the last
|
||||
/// config load attempt.
|
||||
ConfigLoaded {
|
||||
/// Whether the loading failed.
|
||||
///
|
||||
/// For example, the config file couldn't be parsed.
|
||||
failed: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl FromStr for WorkspaceReferenceArg {
|
||||
|
||||
@@ -43,6 +43,9 @@ pub struct EventStreamState {
|
||||
|
||||
/// State of the overview.
|
||||
pub overview: OverviewState,
|
||||
|
||||
/// State of the config.
|
||||
pub config: ConfigState,
|
||||
}
|
||||
|
||||
/// The workspaces state communicated over the event stream.
|
||||
@@ -73,6 +76,13 @@ pub struct OverviewState {
|
||||
pub is_open: bool,
|
||||
}
|
||||
|
||||
/// The config state communicated over the event stream.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ConfigState {
|
||||
/// Whether the last config load attempt had failed.
|
||||
pub failed: bool,
|
||||
}
|
||||
|
||||
impl EventStreamStatePart for EventStreamState {
|
||||
fn replicate(&self) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
@@ -80,6 +90,7 @@ impl EventStreamStatePart for EventStreamState {
|
||||
events.extend(self.windows.replicate());
|
||||
events.extend(self.keyboard_layouts.replicate());
|
||||
events.extend(self.overview.replicate());
|
||||
events.extend(self.config.replicate());
|
||||
events
|
||||
}
|
||||
|
||||
@@ -88,6 +99,7 @@ impl EventStreamStatePart for EventStreamState {
|
||||
let event = self.windows.apply(event)?;
|
||||
let event = self.keyboard_layouts.apply(event)?;
|
||||
let event = self.overview.apply(event)?;
|
||||
let event = self.config.apply(event)?;
|
||||
Some(event)
|
||||
}
|
||||
}
|
||||
@@ -189,6 +201,13 @@ impl EventStreamStatePart for WindowsState {
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::WindowLayoutsChanged { changes } => {
|
||||
for (id, update) in changes {
|
||||
let win = self.windows.get_mut(&id);
|
||||
let win = win.expect("changed window was missing from the map");
|
||||
win.layout = update;
|
||||
}
|
||||
}
|
||||
event => return Some(event),
|
||||
}
|
||||
None
|
||||
@@ -237,3 +256,21 @@ impl EventStreamStatePart for OverviewState {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl EventStreamStatePart for ConfigState {
|
||||
fn replicate(&self) -> Vec<Event> {
|
||||
vec![Event::ConfigLoaded {
|
||||
failed: self.failed,
|
||||
}]
|
||||
}
|
||||
|
||||
fn apply(&mut self, event: Event) -> Option<Event> {
|
||||
match event {
|
||||
Event::ConfigLoaded { failed } => {
|
||||
self.failed = failed;
|
||||
}
|
||||
event => return Some(event),
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ repository.workspace = true
|
||||
[dependencies]
|
||||
adw = { version = "0.7.2", package = "libadwaita", features = ["v1_4"] }
|
||||
anyhow.workspace = true
|
||||
gtk = { version = "0.9.6", package = "gtk4", features = ["v4_12"] }
|
||||
gtk = { version = "0.9.7", package = "gtk4", features = ["v4_12"] }
|
||||
niri = { version = "25.5.1", path = ".." }
|
||||
niri-config = { version = "25.5.1", path = "../niri-config" }
|
||||
smithay.workspace = true
|
||||
|
||||
@@ -36,6 +36,7 @@ impl Layout {
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: String::new(),
|
||||
model: String::new(),
|
||||
serial_number: String::new(),
|
||||
},
|
||||
);
|
||||
let mode = Some(Mode {
|
||||
|
||||
@@ -255,10 +255,6 @@ impl LayoutElement for TestWindow {
|
||||
&EMPTY
|
||||
}
|
||||
|
||||
fn animation_snapshot(&self) -> Option<&LayoutElementRenderSnapshot> {
|
||||
None
|
||||
}
|
||||
|
||||
fn take_animation_snapshot(&mut self) -> Option<LayoutElementRenderSnapshot> {
|
||||
None
|
||||
}
|
||||
|
||||
+3
-1
@@ -76,6 +76,8 @@ BuildRequires: pango-devel
|
||||
BuildRequires: cairo-gobject-devel
|
||||
# Needed for pipewire-rs
|
||||
BuildRequires: clang
|
||||
# Needed for some tests with a surfaceless EGL renderer
|
||||
BuildRequires: mesa-libEGL
|
||||
|
||||
Requires: mesa-dri-drivers
|
||||
Requires: mesa-libEGL
|
||||
@@ -136,7 +138,7 @@ install -Dm644 -t %{buildroot}%{_userunitdir} ./resources/niri-shutdown.target
|
||||
%license LICENSE
|
||||
%doc README.md
|
||||
%doc resources/default-config.kdl
|
||||
%doc wiki
|
||||
%doc docs/wiki
|
||||
%{_bindir}/niri
|
||||
%{_bindir}/niri-session
|
||||
%{_datadir}/wayland-sessions/niri.desktop
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// This config is in the KDL format: https://kdl.dev
|
||||
// "/-" comments out the following node.
|
||||
// Check the wiki for a full description of the configuration:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction
|
||||
// https://yalter.github.io/niri/Configuration:-Introduction
|
||||
|
||||
// Input device configuration.
|
||||
// Find the full list of options on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Input
|
||||
// https://yalter.github.io/niri/Configuration:-Input
|
||||
input {
|
||||
keyboard {
|
||||
xkb {
|
||||
@@ -15,6 +15,10 @@ input {
|
||||
// For example:
|
||||
// layout "us,ru"
|
||||
// options "grp:win_space_toggle,compose:ralt,ctrl:nocaps"
|
||||
|
||||
// If this section is empty, niri will fetch xkb settings
|
||||
// from org.freedesktop.locale1. You can control these using
|
||||
// localectl set-x11-keymap.
|
||||
}
|
||||
|
||||
// Enable numlock on startup, omitting this setting disables it.
|
||||
@@ -23,6 +27,7 @@ input {
|
||||
|
||||
// Next sections include libinput settings.
|
||||
// Omitting settings disables them, or leaves them at their default values.
|
||||
// All commented-out settings here are examples, not defaults.
|
||||
touchpad {
|
||||
// off
|
||||
tap
|
||||
@@ -52,6 +57,7 @@ input {
|
||||
// accel-profile "flat"
|
||||
// scroll-method "on-button-down"
|
||||
// scroll-button 273
|
||||
// scroll-button-lock
|
||||
// middle-emulation
|
||||
}
|
||||
|
||||
@@ -67,7 +73,7 @@ input {
|
||||
// by running `niri msg outputs` while inside a niri instance.
|
||||
// The built-in laptop monitor is usually called "eDP-1".
|
||||
// Find more information on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs
|
||||
// https://yalter.github.io/niri/Configuration:-Outputs
|
||||
// Remember to uncomment the node by removing "/-"!
|
||||
/-output "eDP-1" {
|
||||
// Uncomment this line to disable this output.
|
||||
@@ -102,7 +108,7 @@ input {
|
||||
|
||||
// Settings that influence how windows are positioned and sized.
|
||||
// Find more information on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout
|
||||
// https://yalter.github.io/niri/Configuration:-Layout
|
||||
layout {
|
||||
// Set gaps around windows in logical pixels.
|
||||
gaps 16
|
||||
@@ -164,6 +170,9 @@ layout {
|
||||
active-color "#7fc8ff"
|
||||
|
||||
// Color of the ring on inactive monitors.
|
||||
//
|
||||
// The focus ring only draws around the active window, so the only place
|
||||
// where you can see its inactive-color is on other monitors.
|
||||
inactive-color "#505050"
|
||||
|
||||
// You can also use gradients. They take precedence over solid colors.
|
||||
@@ -173,7 +182,7 @@ layout {
|
||||
// You can use any CSS linear-gradient tool on the web to set these up.
|
||||
// Changing the color space is also supported, check the wiki for more info.
|
||||
//
|
||||
// active-gradient from="#80c8ff" to="#bbddff" angle=45
|
||||
// active-gradient from="#80c8ff" to="#c7ff7f" angle=45
|
||||
|
||||
// You can also color the gradient relative to the entire view
|
||||
// of the workspace, rather than relative to just the window itself.
|
||||
@@ -195,7 +204,11 @@ layout {
|
||||
// Color of the border around windows that request your attention.
|
||||
urgent-color "#9b0000"
|
||||
|
||||
// active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view"
|
||||
// Gradients can use a few different interpolation color spaces.
|
||||
// For example, this is a pastel rainbow gradient via in="oklch longer hue".
|
||||
//
|
||||
// active-gradient from="#e5989b" to="#ffb4a2" angle=45 relative-to="workspace-view" in="oklch longer hue"
|
||||
|
||||
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
|
||||
}
|
||||
|
||||
@@ -211,13 +224,13 @@ layout {
|
||||
// radius. It has to assume that windows have square corners, leading to
|
||||
// shadow artifacts inside the CSD rounded corners. This setting fixes
|
||||
// those artifacts.
|
||||
//
|
||||
//
|
||||
// However, instead you may want to set prefer-no-csd and/or
|
||||
// geometry-corner-radius. Then, niri will know the corner radius and
|
||||
// draw the shadow correctly, without having to draw it behind the
|
||||
// window. These will also remove client-side shadows if the window
|
||||
// draws any.
|
||||
//
|
||||
//
|
||||
// draw-behind-window true
|
||||
|
||||
// You can change how shadows look. The values below are in logical
|
||||
@@ -257,6 +270,14 @@ layout {
|
||||
// This line starts waybar, a commonly used bar for Wayland compositors.
|
||||
spawn-at-startup "waybar"
|
||||
|
||||
// To run a shell command (with variables, pipes, etc.), use spawn-sh-at-startup:
|
||||
// spawn-sh-at-startup "qs -c ~/source/qs/MyAwesomeShell"
|
||||
|
||||
hotkey-overlay {
|
||||
// Uncomment this line to disable the "Important Hotkeys" pop-up at startup.
|
||||
// skip-at-startup
|
||||
}
|
||||
|
||||
// Uncomment this line to ask the clients to omit their client-side decorations if possible.
|
||||
// If the client will specifically ask for CSD, the request will be honored.
|
||||
// Additionally, clients will be informed that they are tiled, removing some client-side rounded corners.
|
||||
@@ -274,7 +295,7 @@ screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
|
||||
|
||||
// Animation settings.
|
||||
// The wiki explains how to configure individual animations:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Animations
|
||||
// https://yalter.github.io/niri/Configuration:-Animations
|
||||
animations {
|
||||
// Uncomment to turn off all animations.
|
||||
// off
|
||||
@@ -285,7 +306,7 @@ animations {
|
||||
|
||||
// Window rules let you adjust behavior for individual windows.
|
||||
// Find more information on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules
|
||||
// https://yalter.github.io/niri/Configuration:-Window-Rules
|
||||
|
||||
// Work around WezTerm's initial configure bug
|
||||
// by setting an empty default-column-width.
|
||||
@@ -345,10 +366,6 @@ binds {
|
||||
Mod+D hotkey-overlay-title="Run an Application: fuzzel" { spawn "fuzzel"; }
|
||||
Super+Alt+L hotkey-overlay-title="Lock the Screen: swaylock" { spawn "swaylock"; }
|
||||
|
||||
// You can also use a shell. Do this if you need pipes, multiple commands, etc.
|
||||
// Note: the entire command goes as a single argument in the end.
|
||||
// Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; }
|
||||
|
||||
// Example volume keys mappings for PipeWire & WirePlumber.
|
||||
// The allow-when-locked=true property makes them work even when the session is locked.
|
||||
XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; }
|
||||
@@ -356,12 +373,21 @@ binds {
|
||||
XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
|
||||
XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
|
||||
|
||||
// Example brightness key mappings for brightnessctl.
|
||||
XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+10%"; }
|
||||
XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "10%-"; }
|
||||
|
||||
// Use spawn-sh to run a shell command. Do this if you need pipes, multiple commands, etc.
|
||||
// Note: the entire command goes as a single argument. It's passed verbatim to `sh -c`.
|
||||
// For example, this is a standard bind to toggle the screen reader (orca).
|
||||
Super+Alt+S allow-when-locked=true hotkey-overlay-title=null { spawn-sh "pkill orca || exec orca"; }
|
||||
|
||||
// Open/close the Overview: a zoomed-out view of workspaces and windows.
|
||||
// You can also move the mouse into the top-left hot corner,
|
||||
// or do a four-finger swipe up on a touchpad.
|
||||
Mod+O repeat=false { toggle-overview; }
|
||||
|
||||
Mod+Q { close-window; }
|
||||
Mod+Q repeat=false { close-window; }
|
||||
|
||||
Mod+Left { focus-column-left; }
|
||||
Mod+Down { focus-window-down; }
|
||||
|
||||
+11
-1
@@ -1,5 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Detect if being run as a user service, which implies external session management,
|
||||
# exec compositor directly
|
||||
if [ -n "${MANAGERPID:-}" ] && [ "${SYSTEMD_EXEC_PID:-}" = "$$" ]; then
|
||||
case "$(ps -p "$MANAGERPID" -o cmd=)" in
|
||||
*systemd*--user*)
|
||||
exec niri --session
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -n "$SHELL" ] &&
|
||||
grep -q "$SHELL" /etc/shells &&
|
||||
! (echo "$SHELL" | grep -q "false") &&
|
||||
@@ -40,7 +50,7 @@ if hash systemctl >/dev/null 2>&1; then
|
||||
systemctl --user start --job-mode=replace-irreversibly niri-shutdown.target
|
||||
|
||||
# Unset environment that we've set.
|
||||
systemctl --user unset-environment WAYLAND_DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP NIRI_SOCKET
|
||||
systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP NIRI_SOCKET
|
||||
elif hash dinitctl >/dev/null 2>&1; then
|
||||
# Check that the user dinit daemon is running
|
||||
if ! pgrep -u "$(id -u)" dinit >/dev/null 2>&1; then
|
||||
|
||||
+30
-3
@@ -1,13 +1,16 @@
|
||||
//! Headless backend for tests.
|
||||
//!
|
||||
//! This can eventually grow into a more complete backend if needed, but for now it's missing some
|
||||
//! crucial parts like rendering.
|
||||
//! crucial parts like dmabufs.
|
||||
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use niri_config::OutputName;
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::egl::native::EGLSurfacelessDisplay;
|
||||
use smithay::backend::egl::{EGLContext, EGLDisplay};
|
||||
use smithay::backend::renderer::element::RenderElementStates;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::output::{Mode, Output, PhysicalProperties, Subpixel};
|
||||
@@ -17,21 +20,44 @@ use smithay::wayland::presentation::Refresh;
|
||||
|
||||
use super::{IpcOutputMap, OutputId, RenderResult};
|
||||
use crate::niri::{Niri, RedrawState};
|
||||
use crate::render_helpers::{resources, shaders};
|
||||
use crate::utils::{get_monotonic_time, logical_output};
|
||||
|
||||
pub struct Headless {
|
||||
renderer: Option<GlesRenderer>,
|
||||
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
|
||||
}
|
||||
|
||||
impl Headless {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
renderer: None,
|
||||
ipc_outputs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self, _niri: &mut Niri) {}
|
||||
|
||||
pub fn add_renderer(&mut self) -> anyhow::Result<()> {
|
||||
if self.renderer.is_some() {
|
||||
error!("add_renderer: renderer must not already exist");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut renderer = unsafe {
|
||||
let display =
|
||||
EGLDisplay::new(EGLSurfacelessDisplay).context("error creating EGL display")?;
|
||||
let context = EGLContext::new(&display).context("error creating EGL context")?;
|
||||
GlesRenderer::new(context).context("error creating renderer")?
|
||||
};
|
||||
|
||||
resources::init(&mut renderer);
|
||||
shaders::init(&mut renderer);
|
||||
|
||||
self.renderer = Some(renderer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_output(&mut self, niri: &mut Niri, n: u8, size: (u16, u16)) {
|
||||
let connector = format!("headless-{n}");
|
||||
let make = "niri".to_string();
|
||||
@@ -45,6 +71,7 @@ impl Headless {
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: make.clone(),
|
||||
model: model.clone(),
|
||||
serial_number: serial.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -93,9 +120,9 @@ impl Headless {
|
||||
|
||||
pub fn with_primary_renderer<T>(
|
||||
&mut self,
|
||||
_f: impl FnOnce(&mut GlesRenderer) -> T,
|
||||
f: impl FnOnce(&mut GlesRenderer) -> T,
|
||||
) -> Option<T> {
|
||||
None
|
||||
self.renderer.as_mut().map(f)
|
||||
}
|
||||
|
||||
pub fn render(&mut self, niri: &mut Niri, output: &Output) -> RenderResult {
|
||||
|
||||
+25
-10
@@ -316,11 +316,11 @@ impl Tty {
|
||||
|
||||
let mut node_path = String::new();
|
||||
if let Some(path) = primary_render_node.dev_path() {
|
||||
write!(node_path, "{:?}", path).unwrap();
|
||||
write!(node_path, "{path:?}").unwrap();
|
||||
} else {
|
||||
write!(node_path, "{}", primary_render_node).unwrap();
|
||||
write!(node_path, "{primary_render_node}").unwrap();
|
||||
}
|
||||
info!("using as the render node: {}", node_path);
|
||||
info!("using as the render node: {node_path}");
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
@@ -857,10 +857,13 @@ impl Tty {
|
||||
}
|
||||
debug!("picking mode: {mode:?}");
|
||||
|
||||
// We only use 8888 RGB formats, so set max bpc to 8 to allow more types of links to run.
|
||||
match set_max_bpc(&device.drm, connector.handle(), 8) {
|
||||
Ok(bpc) => debug!("set max bpc to {bpc}"),
|
||||
Err(err) => debug!("error setting max bpc: {err:?}"),
|
||||
if !niri.config.borrow().debug.keep_max_bpc_unchanged {
|
||||
// We only use 8888 RGB formats, so set max bpc to 8 to allow more types of links to
|
||||
// run.
|
||||
match set_max_bpc(&device.drm, connector.handle(), 8) {
|
||||
Ok(bpc) => debug!("set max bpc to {bpc}"),
|
||||
Err(err) => debug!("error setting max bpc: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
let mut gamma_props = GammaProps::new(&device.drm, crtc)
|
||||
@@ -920,6 +923,11 @@ impl Tty {
|
||||
subpixel: connector.subpixel().into(),
|
||||
model: output_name.model.as_deref().unwrap_or("Unknown").to_owned(),
|
||||
make: output_name.make.as_deref().unwrap_or("Unknown").to_owned(),
|
||||
serial_number: output_name
|
||||
.serial
|
||||
.as_deref()
|
||||
.unwrap_or("Unknown")
|
||||
.to_owned(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -972,7 +980,7 @@ impl Tty {
|
||||
surface,
|
||||
None,
|
||||
allocator.clone(),
|
||||
GbmFramebufferExporter::new(device.gbm.clone()),
|
||||
GbmFramebufferExporter::new(device.gbm.clone(), device.render_node.into()),
|
||||
SUPPORTED_COLOR_FORMATS,
|
||||
// This is only used to pick a good internal format, so it can use the surface's render
|
||||
// formats, even though we only ever render on the primary GPU.
|
||||
@@ -1002,7 +1010,7 @@ impl Tty {
|
||||
surface,
|
||||
None,
|
||||
allocator,
|
||||
GbmFramebufferExporter::new(device.gbm.clone()),
|
||||
GbmFramebufferExporter::new(device.gbm.clone(), device.render_node.into()),
|
||||
SUPPORTED_COLOR_FORMATS,
|
||||
render_formats,
|
||||
device.drm.cursor_size(),
|
||||
@@ -1374,7 +1382,8 @@ impl Tty {
|
||||
span.emit_text(&surface.name.connector);
|
||||
|
||||
if !device.drm.is_active() {
|
||||
warn!("device is inactive");
|
||||
// This branch hits any time we try to render while the user had switched to a
|
||||
// different VT, so don't print anything here.
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1422,6 +1431,12 @@ impl Tty {
|
||||
if debug.disable_cursor_plane {
|
||||
flags.remove(FrameFlags::ALLOW_CURSOR_PLANE_SCANOUT);
|
||||
}
|
||||
if debug.skip_cursor_only_updates_during_vrr {
|
||||
let output_state = niri.output_state.get(output).unwrap();
|
||||
if output_state.frame_clock.vrr() {
|
||||
flags.insert(FrameFlags::SKIP_CURSOR_ONLY_UPDATES);
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ impl Winit {
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: "Smithay".into(),
|
||||
model: "Winit".into(),
|
||||
serial_number: "Unknown".into(),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+26
-1
@@ -56,7 +56,7 @@ pub enum Sub {
|
||||
/// Cause a panic to check if the backtraces are good.
|
||||
Panic,
|
||||
/// Generate shell completions.
|
||||
Completions { shell: Shell },
|
||||
Completions { shell: CompletionShell },
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
@@ -108,3 +108,28 @@ pub enum Msg {
|
||||
/// Print the overview state.
|
||||
OverviewState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, clap::ValueEnum)]
|
||||
pub enum CompletionShell {
|
||||
Bash,
|
||||
Elvish,
|
||||
Fish,
|
||||
PowerShell,
|
||||
Zsh,
|
||||
Nushell,
|
||||
}
|
||||
|
||||
impl TryFrom<CompletionShell> for Shell {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(shell: CompletionShell) -> Result<Self, Self::Error> {
|
||||
match shell {
|
||||
CompletionShell::Bash => Ok(Shell::Bash),
|
||||
CompletionShell::Elvish => Ok(Shell::Elvish),
|
||||
CompletionShell::Fish => Ok(Shell::Fish),
|
||||
CompletionShell::PowerShell => Ok(Shell::PowerShell),
|
||||
CompletionShell::Zsh => Ok(Shell::Zsh),
|
||||
CompletionShell::Nushell => Err("Nushell should be handled separately"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
// References:
|
||||
// - https://invent.kde.org/plasma/kwin/-/blob/397fbbe52a8f2d855ad0c9817b51a9bdf06a68e2/src/a11ykeyboardmonitor.cpp#L41
|
||||
// - https://gitlab.gnome.org/GNOME/mutter/-/blob/cbb7295ac1f93a2dfd55a7c0544688e7e5c4d2e2/src/backends/meta-a11y-manager.c
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use futures_util::StreamExt;
|
||||
use smithay::backend::input::{KeyState, Keycode};
|
||||
use smithay::input::keyboard::{xkb, Keysym};
|
||||
use zbus::blocking::object_server::InterfaceRef;
|
||||
use zbus::fdo::{self, RequestNameFlags};
|
||||
use zbus::interface;
|
||||
use zbus::message::Header;
|
||||
use zbus::names::{BusName, OwnedUniqueName, UniqueName};
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use zbus::zvariant::NoneValue;
|
||||
|
||||
use super::Start;
|
||||
use crate::niri::State;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Data {
|
||||
clients: HashMap<OwnedUniqueName, Client>,
|
||||
|
||||
grabbed_mods: HashSet<Keysym>,
|
||||
grabbed_mod_last_press_time: HashMap<Keysym, Duration>,
|
||||
suppressed_keys: HashSet<Keysym>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Client {
|
||||
watched: bool,
|
||||
grabbed: bool,
|
||||
modifiers: HashSet<Keysym>,
|
||||
keystrokes: Vec<(Keysym, u32)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeyboardMonitor {
|
||||
data: Arc<Mutex<Data>>,
|
||||
iface: Arc<OnceLock<InterfaceRef<Self>>>,
|
||||
}
|
||||
|
||||
/// Interface for monitoring of keyboard input by assistive technologies.
|
||||
///
|
||||
/// This interface is used by assistive technologies to monitor keyboard input of the compositor.
|
||||
/// The compositor is expected to listen on the well-known bus name "org.freedesktop.a11y.Manager"
|
||||
/// at the object path "/org/freedesktop/a11y/Manager".
|
||||
#[interface(name = "org.freedesktop.a11y.KeyboardMonitor")]
|
||||
impl KeyboardMonitor {
|
||||
// Starts grabbing all key events. The client receives the events through the KeyEvent signal,
|
||||
// and in addition, the events aren't handled normally by the compositor. This includes changes
|
||||
// to the state of toggles like Caps Lock, Num Lock, and Scroll Lock.
|
||||
//
|
||||
// This behavior stays in effect until the same client calls UngrabKeyboard or closes its D-Bus
|
||||
// connection.
|
||||
async fn grab_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {
|
||||
let Some(sender) = hdr.sender() else {
|
||||
return Err(fdo::Error::Failed("no sender".to_owned()));
|
||||
};
|
||||
let sender = OwnedUniqueName::from(sender.to_owned());
|
||||
trace!("enabling keyboard grab for {sender}");
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
let client = data.clients.entry(sender).or_default();
|
||||
client.grabbed = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Reverses the effect of calling GrabKeyboard. If GrabKeyboard wasn't previously called, this
|
||||
// method does nothing.
|
||||
//
|
||||
// After calling this method, the key grabs specified in the last call to SetKeyGrabs, if any,
|
||||
// are still in effect. Also, the client will still receive key events through the KeyEvent
|
||||
// signal, if it has called WatchKeyboard.
|
||||
async fn ungrab_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {
|
||||
let Some(sender) = hdr.sender() else {
|
||||
return Err(fdo::Error::Failed("no sender".to_owned()));
|
||||
};
|
||||
let sender = OwnedUniqueName::from(sender.to_owned());
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
if let Some(client) = data.clients.get_mut(&sender) {
|
||||
trace!("disabling keyboard grab for {sender}");
|
||||
client.grabbed = false;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Starts watching all key events. The client receives the events through the KeyEvent signal,
|
||||
// but the events are still handled normally by the compositor. This includes changes to the
|
||||
// state of toggles like Caps Lock, Num Lock, and Scroll Lock.
|
||||
//
|
||||
// This behavior stays in effect until the same client calls UnwatchKeyboard or closes its D-Bus
|
||||
// connection.
|
||||
async fn watch_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {
|
||||
let Some(sender) = hdr.sender() else {
|
||||
return Err(fdo::Error::Failed("no sender".to_owned()));
|
||||
};
|
||||
let sender = OwnedUniqueName::from(sender.to_owned());
|
||||
trace!("enabling keyboard watch for {sender}");
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
let client = data.clients.entry(sender).or_default();
|
||||
client.watched = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Reverses the effect of calling WatchKeyboard. If WatchKeyboard wasn't previously called, this
|
||||
// method does nothing.
|
||||
//
|
||||
// After calling this method, the key grabs specified in the last call to SetKeyGrabs, if any,
|
||||
// are still in effect, but other key events are no longer reported to this client.
|
||||
async fn unwatch_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {
|
||||
let Some(sender) = hdr.sender() else {
|
||||
return Err(fdo::Error::Failed("no sender".to_owned()));
|
||||
};
|
||||
let sender = OwnedUniqueName::from(sender.to_owned());
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
if let Some(client) = data.clients.get_mut(&sender) {
|
||||
trace!("disabling keyboard watch for {sender}");
|
||||
client.watched = false;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Sets the current key grabs for the calling client, overriding any previous call to this
|
||||
// method. For grabbed key events, the KeyEvent signal is emitted, and normal key event handling
|
||||
// is suppressed, including state changes for toggles like Caps Lock and Num Lock.
|
||||
//
|
||||
// The grabs set by this method stay in effect until the same client calls this method again, or
|
||||
// until that client closes its D-Bus connection.
|
||||
//
|
||||
// Each item in `modifiers` is an XKB keysym. All keys in this list will be grabbed, and keys
|
||||
// pressed while any of these keys are down will also be grabbed.
|
||||
//
|
||||
// Each item in `keystrokes` is a struct with the following fields:
|
||||
//
|
||||
// - the XKB keysym of the non-modifier key
|
||||
// - the XKB modifier mask of the modifiers, if any, for this keystroke
|
||||
//
|
||||
// If any of the keys in `modifiers` is pressed alone, the compositor is required to ignore the
|
||||
// key press and release event if a second key press of the same modifier is not received within
|
||||
// a reasonable time frame, for example, the key repeat delay. If such event is received, this
|
||||
// second event is processed normally.
|
||||
async fn set_key_grabs(
|
||||
&self,
|
||||
#[zbus(header)] hdr: Header<'_>,
|
||||
modifiers: Vec<u32>,
|
||||
keystrokes: Vec<(u32, u32)>,
|
||||
) -> fdo::Result<()> {
|
||||
let Some(sender) = hdr.sender() else {
|
||||
return Err(fdo::Error::Failed("no sender".to_owned()));
|
||||
};
|
||||
let sender = OwnedUniqueName::from(sender.to_owned());
|
||||
trace!("updating key grabs for {sender}");
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
let client = data.clients.entry(sender).or_default();
|
||||
client.modifiers = HashSet::from_iter(modifiers.into_iter().map(Keysym::new));
|
||||
client.keystrokes =
|
||||
Vec::from_iter(keystrokes.into_iter().map(|(k, v)| (Keysym::new(k), v)));
|
||||
|
||||
data.rebuild_grabbed_mods();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// The compositor emits this signal for each key press or release.
|
||||
//
|
||||
// - `released`: whether this is a key-up event
|
||||
// - `state`: XKB modifier mask for currently pressed modifiers
|
||||
// - `keysym`: XKB keysym for this key
|
||||
// - `unichar`: Unicode character for this key, or 0 if none
|
||||
// - `keycode`: hardware-dependent keycode for this key
|
||||
#[zbus(signal)]
|
||||
pub async fn key_event(
|
||||
ctxt: &SignalEmitter<'_>,
|
||||
released: bool,
|
||||
state: u32,
|
||||
keysym: u32,
|
||||
unichar: u32,
|
||||
keycode: u16,
|
||||
) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl KeyboardMonitor {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Arc::new(Mutex::new(Data::default())),
|
||||
iface: Arc::new(OnceLock::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_key(
|
||||
&self,
|
||||
repeat_delay: Duration,
|
||||
time: Duration,
|
||||
keycode: Keycode,
|
||||
released: bool,
|
||||
mods: u32,
|
||||
keysym: Keysym,
|
||||
unichar: u32,
|
||||
) -> bool {
|
||||
let _span = tracy_client::span!("KeyboardMonitor::process_key");
|
||||
|
||||
let mut ctxt = self.iface.get().unwrap().signal_emitter().clone();
|
||||
|
||||
let mut data = self.data.lock().unwrap();
|
||||
|
||||
// Emit key events as necessary.
|
||||
for (name, client) in &data.clients {
|
||||
if client.should_watch_keypress(&data.suppressed_keys, mods, keysym) {
|
||||
let _span = tracy_client::span!("emitting key event");
|
||||
|
||||
// Emit to that client only.
|
||||
ctxt = ctxt.set_destination(BusName::Unique(name.as_ref()));
|
||||
let ctxt = &ctxt;
|
||||
async_io::block_on(async move {
|
||||
if let Err(err) = KeyboardMonitor::key_event(
|
||||
ctxt,
|
||||
released,
|
||||
mods,
|
||||
keysym.raw(),
|
||||
unichar,
|
||||
keycode.raw() as u16,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("error emitting key_event: {err:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for double-pressed grabbed modifier that should not be captured.
|
||||
if data.grabbed_mods.contains(&keysym) {
|
||||
if released {
|
||||
// If missing from suppressed keys, then this is a release corresponding to the
|
||||
// second press that got handled normally.
|
||||
if !data.suppressed_keys.contains(&keysym) {
|
||||
trace!("handling release for second press of grabbed modifier: {keysym:?}");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
let last_press_entry = data
|
||||
.grabbed_mod_last_press_time
|
||||
.entry(keysym)
|
||||
.or_insert(Duration::ZERO);
|
||||
let last_press = *last_press_entry;
|
||||
*last_press_entry = time;
|
||||
|
||||
// Modifier pressed twice; handle it as normal.
|
||||
if time <= last_press.saturating_add(repeat_delay) {
|
||||
trace!("handling second press of grabbed modifier: {keysym:?}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut block = false;
|
||||
|
||||
if released {
|
||||
// This is a release for a key that was grabbed.
|
||||
if data.suppressed_keys.remove(&keysym) {
|
||||
trace!("blocking release for previously suppressed key: {keysym:?}");
|
||||
block = true;
|
||||
}
|
||||
} else if data.suppressed_keys.contains(&keysym) {
|
||||
// Second press for an already-pressed key, e.g. from two keyboards.
|
||||
trace!("blocking press for already-pressed key: {keysym:?}");
|
||||
block = true;
|
||||
} else {
|
||||
// Check if it's grabbed by any client.
|
||||
if data
|
||||
.clients
|
||||
.values()
|
||||
.any(|client| client.should_grab_keypress(&data.suppressed_keys, mods, keysym))
|
||||
{
|
||||
trace!("blocking press for grabbed key: {keysym:?}");
|
||||
data.suppressed_keys.insert(keysym);
|
||||
block = true;
|
||||
}
|
||||
}
|
||||
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn rebuild_grabbed_mods(&mut self) {
|
||||
self.grabbed_mods.clear();
|
||||
for client in self.clients.values() {
|
||||
self.grabbed_mods.extend(&client.modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
fn should_grab_keypress(
|
||||
&self,
|
||||
suppressed_keys: &HashSet<Keysym>,
|
||||
mods: u32,
|
||||
keysym: Keysym,
|
||||
) -> bool {
|
||||
// Grabbing all keys.
|
||||
if self.grabbed {
|
||||
return true;
|
||||
}
|
||||
|
||||
for modifier in &self.modifiers {
|
||||
// This is a grabbed modifier, or a grabbed modifier is currently down.
|
||||
if *modifier == keysym || suppressed_keys.contains(modifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (grabbed_keysym, grabbed_mods) in &self.keystrokes {
|
||||
// This is a grabbed keystroke.
|
||||
if *grabbed_keysym == keysym && *grabbed_mods == mods {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn should_watch_keypress(
|
||||
&self,
|
||||
suppressed_keys: &HashSet<Keysym>,
|
||||
mods: u32,
|
||||
keysym: Keysym,
|
||||
) -> bool {
|
||||
if self.watched {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.should_grab_keypress(suppressed_keys, mods, keysym)
|
||||
}
|
||||
}
|
||||
|
||||
async fn monitor_disappeared_clients(
|
||||
conn: &zbus::Connection,
|
||||
data: Arc<Mutex<Data>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let proxy = fdo::DBusProxy::new(conn)
|
||||
.await
|
||||
.context("error creating a DBusProxy")?;
|
||||
|
||||
let mut stream = proxy
|
||||
.receive_name_owner_changed_with_args(&[(2, UniqueName::null_value())])
|
||||
.await
|
||||
.context("error creating a NameOwnerChanged stream")?;
|
||||
|
||||
while let Some(signal) = stream.next().await {
|
||||
let args = signal
|
||||
.args()
|
||||
.context("error retrieving NameOwnerChanged args")?;
|
||||
|
||||
let Some(name) = &**args.old_owner() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if args.new_owner().is_none() {
|
||||
trace!("keyboard monitor client disconnected: {name}");
|
||||
|
||||
let name = OwnedUniqueName::from(name.to_owned());
|
||||
let mut data = data.lock().unwrap();
|
||||
data.clients.remove(&name);
|
||||
data.rebuild_grabbed_mods();
|
||||
} else {
|
||||
error!("non-null new_owner should've been filtered out");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Start for KeyboardMonitor {
|
||||
fn start(self) -> anyhow::Result<zbus::blocking::Connection> {
|
||||
let data = self.data.clone();
|
||||
|
||||
let conn = zbus::blocking::Connection::session()?;
|
||||
let flags = RequestNameFlags::AllowReplacement
|
||||
| RequestNameFlags::ReplaceExisting
|
||||
| RequestNameFlags::DoNotQueue;
|
||||
|
||||
conn.object_server()
|
||||
.at("/org/freedesktop/a11y/Manager", self.clone())?;
|
||||
conn.request_name_with_flags("org.freedesktop.a11y.Manager", flags)?;
|
||||
|
||||
let iface = conn
|
||||
.object_server()
|
||||
.interface("/org/freedesktop/a11y/Manager")?;
|
||||
let _ = self.iface.set(iface);
|
||||
|
||||
let async_conn = conn.inner().clone();
|
||||
let future = async move {
|
||||
if let Err(err) = monitor_disappeared_clients(&async_conn, data.clone()).await {
|
||||
warn!("error monitoring keyboard monitor clients: {err:?}");
|
||||
|
||||
// Since the monitor is now broken, prevent any further communication.
|
||||
if let Err(err) = async_conn.close().await {
|
||||
warn!("error closing connection: {err:?}");
|
||||
}
|
||||
|
||||
let mut data = data.lock().unwrap();
|
||||
data.clients.clear();
|
||||
data.rebuild_grabbed_mods();
|
||||
}
|
||||
};
|
||||
let task = conn
|
||||
.inner()
|
||||
.executor()
|
||||
.spawn(future, "monitor disappearing keyboard clients");
|
||||
task.detach();
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn a11y_process_key(&mut self, time: Duration, keycode: Keycode, state: KeyState) -> bool {
|
||||
if self.niri.a11y_keyboard_monitor.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let keyboard = self.niri.seat.get_keyboard().unwrap();
|
||||
|
||||
let (mods, keysym, unichar) = keyboard.with_xkb_state(self, |context| {
|
||||
let xkb = context.xkb().lock().unwrap();
|
||||
// SAFETY: we're not changing the ref count.
|
||||
let state = unsafe { xkb.state() };
|
||||
|
||||
let keysym = state.key_get_one_sym(keycode);
|
||||
let mods = state.serialize_mods(xkb::STATE_MODS_EFFECTIVE);
|
||||
let unichar = state.key_get_utf32(keycode);
|
||||
|
||||
(mods, keysym, unichar)
|
||||
});
|
||||
|
||||
let config = self.niri.config.borrow();
|
||||
let repeat_delay = Duration::from_millis(u64::from(config.input.keyboard.repeat_delay));
|
||||
let released = state == KeyState::Released;
|
||||
|
||||
let Some(monitor) = &self.niri.a11y_keyboard_monitor else {
|
||||
return false;
|
||||
};
|
||||
monitor.process_key(repeat_delay, time, keycode, released, mods, keysym, unichar)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use futures_util::StreamExt;
|
||||
use niri_config::Xkb;
|
||||
use zbus::names::InterfaceName;
|
||||
use zbus::{fdo, zvariant};
|
||||
|
||||
pub enum Locale1ToNiri {
|
||||
XkbChanged(Xkb),
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
to_niri: calloop::channel::Sender<Locale1ToNiri>,
|
||||
) -> anyhow::Result<zbus::blocking::Connection> {
|
||||
let conn = zbus::blocking::Connection::system()?;
|
||||
|
||||
let async_conn = conn.inner().clone();
|
||||
let future = async move {
|
||||
let proxy = fdo::PropertiesProxy::new(
|
||||
&async_conn,
|
||||
"org.freedesktop.locale1",
|
||||
"/org/freedesktop/locale1",
|
||||
)
|
||||
.await;
|
||||
let proxy = match proxy {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
warn!("error creating PropertiesProxy: {err:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut props_changed = match proxy.receive_properties_changed().await {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
warn!("error subscribing to PropertiesChanged: {err:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let props = proxy
|
||||
.get_all(InterfaceName::try_from("org.freedesktop.locale1").unwrap())
|
||||
.await;
|
||||
let mut props = match props {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
warn!("error receiving initial properties: {err:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("initial properties: {props:?}");
|
||||
|
||||
let mut get = |name| {
|
||||
props
|
||||
.remove(name)
|
||||
.and_then(|x| String::try_from(x).ok())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let mut xkb = Xkb {
|
||||
rules: String::new(),
|
||||
model: get("X11Model"),
|
||||
layout: get("X11Layout"),
|
||||
variant: get("X11Variant"),
|
||||
options: match get("X11Options") {
|
||||
x if x.is_empty() => None,
|
||||
x => Some(x),
|
||||
},
|
||||
file: None,
|
||||
};
|
||||
|
||||
// Send the initial properties.
|
||||
if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) {
|
||||
warn!("error sending message to niri: {err:?}");
|
||||
return;
|
||||
};
|
||||
|
||||
while let Some(changed) = props_changed.next().await {
|
||||
let args = match changed.args() {
|
||||
Ok(args) => args,
|
||||
Err(err) => {
|
||||
warn!("error parsing locale1 PropertiesChanged args: {err:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut changed = false;
|
||||
for (name, value) in args.changed_properties() {
|
||||
trace!("changed property: {name} => {value:?}");
|
||||
|
||||
let value = zvariant::Str::try_from(value).unwrap_or_default();
|
||||
let value = value.as_str();
|
||||
|
||||
match *name {
|
||||
"X11Model" => {
|
||||
if xkb.model != value {
|
||||
xkb.model = String::from(value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
"X11Layout" => {
|
||||
if xkb.layout != value {
|
||||
xkb.layout = String::from(value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
"X11Variant" => {
|
||||
if xkb.variant != value {
|
||||
xkb.variant = String::from(value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
"X11Options" => {
|
||||
let value = match value {
|
||||
"" => None,
|
||||
x => Some(x),
|
||||
};
|
||||
if xkb.options.as_deref() != value {
|
||||
xkb.options = value.map(String::from);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if !changed {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) {
|
||||
warn!("error sending message to niri: {err:?}");
|
||||
return;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let task = conn
|
||||
.inner()
|
||||
.executor()
|
||||
.spawn(future, "monitor locale1 property changes");
|
||||
task.detach();
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use anyhow::Context;
|
||||
@@ -13,11 +13,12 @@ use zbus::{interface, Task};
|
||||
|
||||
use super::Start;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScreenSaver {
|
||||
is_inhibited: Arc<AtomicBool>,
|
||||
is_broken: Arc<AtomicBool>,
|
||||
inhibitors: Arc<Mutex<HashMap<u32, OwnedUniqueName>>>,
|
||||
counter: u32,
|
||||
counter: Arc<AtomicU32>,
|
||||
monitor_task: Arc<OnceLock<Task<()>>>,
|
||||
}
|
||||
|
||||
@@ -43,16 +44,16 @@ impl ScreenSaver {
|
||||
|
||||
let mut cookie = None;
|
||||
for _ in 0..3 {
|
||||
// Start from 1 because some clients don't like 0.
|
||||
self.counter = self.counter.wrapping_add(1);
|
||||
if self.counter == 0 {
|
||||
self.counter += 1;
|
||||
let mut inhibitor_key = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
if inhibitor_key == 0 {
|
||||
// Some clients don't like 0, add one more.
|
||||
inhibitor_key = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Entry::Vacant(entry) = inhibitors.entry(self.counter) {
|
||||
if let Entry::Vacant(entry) = inhibitors.entry(inhibitor_key) {
|
||||
entry.insert(name);
|
||||
self.is_inhibited.store(true, Ordering::SeqCst);
|
||||
cookie = Some(self.counter);
|
||||
let _ = cookie.insert(inhibitor_key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +84,8 @@ impl ScreenSaver {
|
||||
is_inhibited,
|
||||
is_broken: Arc::new(AtomicBool::new(false)),
|
||||
inhibitors: Arc::new(Mutex::new(HashMap::new())),
|
||||
counter: 0,
|
||||
// Start from 1 because some clients don't like 0.
|
||||
counter: Arc::new(AtomicU32::new(1)),
|
||||
monitor_task: Arc::new(OnceLock::new()),
|
||||
}
|
||||
}
|
||||
@@ -138,8 +140,15 @@ impl Start for ScreenSaver {
|
||||
| RequestNameFlags::ReplaceExisting
|
||||
| RequestNameFlags::DoNotQueue;
|
||||
|
||||
conn.object_server()
|
||||
.at("/org/freedesktop/ScreenSaver", self)?;
|
||||
let org_fd_ss_registered = conn
|
||||
.object_server()
|
||||
.at("/org/freedesktop/ScreenSaver", self.clone())?;
|
||||
let ss_registered = conn.object_server().at("/ScreenSaver", self)?;
|
||||
|
||||
if !org_fd_ss_registered && !ss_registered {
|
||||
anyhow::bail!("failed to register any org.freedesktop.ScreenSaver interface")
|
||||
}
|
||||
|
||||
conn.request_name_with_flags("org.freedesktop.ScreenSaver", flags)?;
|
||||
|
||||
let async_conn = conn.inner();
|
||||
|
||||
@@ -3,6 +3,8 @@ use zbus::object_server::Interface;
|
||||
|
||||
use crate::niri::State;
|
||||
|
||||
pub mod freedesktop_a11y;
|
||||
pub mod freedesktop_locale1;
|
||||
pub mod freedesktop_screensaver;
|
||||
pub mod gnome_shell_introspect;
|
||||
pub mod gnome_shell_screenshot;
|
||||
@@ -14,6 +16,7 @@ pub mod mutter_screen_cast;
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
use mutter_screen_cast::ScreenCast;
|
||||
|
||||
use self::freedesktop_a11y::KeyboardMonitor;
|
||||
use self::freedesktop_screensaver::ScreenSaver;
|
||||
use self::gnome_shell_introspect::Introspect;
|
||||
use self::mutter_display_config::DisplayConfig;
|
||||
@@ -32,6 +35,8 @@ pub struct DBusServers {
|
||||
pub conn_introspect: Option<Connection>,
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
pub conn_screen_cast: Option<Connection>,
|
||||
pub conn_locale1: Option<Connection>,
|
||||
pub conn_keyboard_monitor: Option<Connection>,
|
||||
}
|
||||
|
||||
impl DBusServers {
|
||||
@@ -123,6 +128,28 @@ impl DBusServers {
|
||||
let screen_cast = ScreenCast::new(backend.ipc_outputs(), to_niri);
|
||||
dbus.conn_screen_cast = try_start(screen_cast);
|
||||
}
|
||||
|
||||
let keyboard_monitor = KeyboardMonitor::new();
|
||||
if let Some(x) = try_start(keyboard_monitor.clone()) {
|
||||
dbus.conn_keyboard_monitor = Some(x);
|
||||
niri.a11y_keyboard_monitor = Some(keyboard_monitor);
|
||||
}
|
||||
}
|
||||
|
||||
let (to_niri, from_locale1) = calloop::channel::channel();
|
||||
niri.event_loop
|
||||
.insert_source(from_locale1, move |event, _, state| match event {
|
||||
calloop::channel::Event::Msg(msg) => state.on_locale1_msg(msg),
|
||||
calloop::channel::Event::Closed => (),
|
||||
})
|
||||
.unwrap();
|
||||
match freedesktop_locale1::start(to_niri) {
|
||||
Ok(conn) => {
|
||||
dbus.conn_locale1 = Some(conn);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("error starting locale1 watcher: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
niri.dbus = Some(dbus);
|
||||
|
||||
@@ -189,8 +189,7 @@ impl DisplayConfig {
|
||||
for (connector, mode, _props) in requested_config.monitors {
|
||||
if !current_conf.values().any(|o| o.name == connector) {
|
||||
return Err(zbus::fdo::Error::Failed(format!(
|
||||
"Connector '{}' not found",
|
||||
connector
|
||||
"Connector '{connector}' not found",
|
||||
)));
|
||||
}
|
||||
new_conf.insert(
|
||||
@@ -210,8 +209,7 @@ impl DisplayConfig {
|
||||
7 => niri_ipc::Transform::Flipped270,
|
||||
x => {
|
||||
return Err(zbus::fdo::Error::Failed(format!(
|
||||
"Unknown transform {}",
|
||||
x
|
||||
"Unknown transform {x}",
|
||||
)))
|
||||
}
|
||||
},
|
||||
@@ -220,10 +218,7 @@ impl DisplayConfig {
|
||||
y: requested_config.y,
|
||||
}),
|
||||
mode: Some(niri_ipc::ConfiguredMode::from_str(&mode).map_err(|e| {
|
||||
zbus::fdo::Error::Failed(format!(
|
||||
"Could not parse mode '{}': {}",
|
||||
mode, e
|
||||
))
|
||||
zbus::fdo::Error::Failed(format!("Could not parse mode '{mode}': {e}"))
|
||||
})?),
|
||||
// FIXME: VRR
|
||||
..Default::default()
|
||||
|
||||
@@ -120,7 +120,7 @@ impl ScreenCast {
|
||||
|
||||
static NUMBER: AtomicUsize = AtomicUsize::new(0);
|
||||
let session_id = NUMBER.fetch_add(1, Ordering::SeqCst);
|
||||
let path = format!("/org/gnome/Mutter/ScreenCast/Session/u{}", session_id);
|
||||
let path = format!("/org/gnome/Mutter/ScreenCast/Session/u{session_id}");
|
||||
let path = OwnedObjectPath::try_from(path).unwrap();
|
||||
|
||||
let session = Session::new(session_id, self.ipc_outputs.clone(), self.to_niri.clone());
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use smithay::backend::input::KeyState;
|
||||
use smithay::desktop::PopupKind;
|
||||
use smithay::input::keyboard::{KeyboardTarget, KeysymHandle, ModifiersState};
|
||||
use smithay::input::pointer::{self, PointerTarget};
|
||||
use smithay::input::Seat;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{IsAlive, Serial};
|
||||
use smithay::wayland::seat::WaylandFocus;
|
||||
|
||||
use crate::niri::State;
|
||||
use crate::ui::screenshot_ui::ScreenshotUiFocusTarget;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum KeyboardFocusTarget {
|
||||
Surface(WlSurface),
|
||||
ScreenshotUi(ScreenshotUiFocusTarget),
|
||||
Overview,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PointerFocusTarget {
|
||||
Surface(WlSurface),
|
||||
ScreenshotUi(ScreenshotUiFocusTarget),
|
||||
Overview,
|
||||
}
|
||||
|
||||
impl KeyboardFocusTarget {
|
||||
fn inner(&self) -> &dyn KeyboardTarget<State> {
|
||||
match self {
|
||||
Self::Surface(surface) => surface,
|
||||
Self::ScreenshotUi(x) => x,
|
||||
Self::Overview => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerFocusTarget {
|
||||
fn inner(&self) -> &dyn PointerTarget<State> {
|
||||
match self {
|
||||
Self::Surface(surface) => surface,
|
||||
Self::ScreenshotUi(x) => x,
|
||||
Self::Overview => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface(&self) -> Option<&WlSurface> {
|
||||
match self {
|
||||
PointerFocusTarget::Surface(surface) => Some(surface),
|
||||
PointerFocusTarget::ScreenshotUi(_) => None,
|
||||
PointerFocusTarget::Overview => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyboardFocusTarget> for PointerFocusTarget {
|
||||
fn from(value: KeyboardFocusTarget) -> Self {
|
||||
match value {
|
||||
KeyboardFocusTarget::Surface(surface) => Self::Surface(surface),
|
||||
KeyboardFocusTarget::ScreenshotUi(x) => Self::ScreenshotUi(x),
|
||||
KeyboardFocusTarget::Overview => Self::Overview,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PopupKind> for KeyboardFocusTarget {
|
||||
fn from(value: PopupKind) -> Self {
|
||||
Self::Surface(value.wl_surface().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandFocus for KeyboardFocusTarget {
|
||||
fn wl_surface(&self) -> Option<Cow<'_, WlSurface>> {
|
||||
match self {
|
||||
Self::Surface(surface) => Some(surface),
|
||||
Self::ScreenshotUi(_) => None,
|
||||
Self::Overview => None,
|
||||
}
|
||||
.map(Cow::Borrowed)
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandFocus for PointerFocusTarget {
|
||||
fn wl_surface(&self) -> Option<Cow<'_, WlSurface>> {
|
||||
self.surface().map(Cow::Borrowed)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for KeyboardFocusTarget {
|
||||
fn alive(&self) -> bool {
|
||||
match self {
|
||||
Self::Surface(surface) => surface.alive(),
|
||||
Self::ScreenshotUi(_) => true,
|
||||
Self::Overview => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for PointerFocusTarget {
|
||||
fn alive(&self) -> bool {
|
||||
match self {
|
||||
Self::Surface(surface) => surface.alive(),
|
||||
Self::ScreenshotUi(_) => true,
|
||||
Self::Overview => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardTarget<State> for KeyboardFocusTarget {
|
||||
fn enter(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
keys: Vec<KeysymHandle<'_>>,
|
||||
serial: Serial,
|
||||
) {
|
||||
self.inner().enter(seat, data, keys, serial);
|
||||
}
|
||||
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
||||
self.inner().leave(seat, data, serial);
|
||||
}
|
||||
|
||||
fn key(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
key: KeysymHandle<'_>,
|
||||
state: KeyState,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
self.inner().key(seat, data, key, state, serial, time);
|
||||
}
|
||||
|
||||
fn modifiers(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
) {
|
||||
self.inner().modifiers(seat, data, modifiers, serial);
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerTarget<State> for PointerFocusTarget {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &pointer::MotionEvent) {
|
||||
self.inner().enter(seat, data, event);
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &pointer::MotionEvent) {
|
||||
self.inner().motion(seat, data, event);
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::RelativeMotionEvent,
|
||||
) {
|
||||
self.inner().relative_motion(seat, data, event);
|
||||
}
|
||||
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &pointer::ButtonEvent) {
|
||||
self.inner().button(seat, data, event);
|
||||
}
|
||||
|
||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: pointer::AxisFrame) {
|
||||
self.inner().axis(seat, data, frame);
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State) {
|
||||
self.inner().frame(seat, data);
|
||||
}
|
||||
|
||||
fn gesture_swipe_begin(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GestureSwipeBeginEvent,
|
||||
) {
|
||||
self.inner().gesture_swipe_begin(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_swipe_update(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GestureSwipeUpdateEvent,
|
||||
) {
|
||||
self.inner().gesture_swipe_update(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_swipe_end(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GestureSwipeEndEvent,
|
||||
) {
|
||||
self.inner().gesture_swipe_end(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_pinch_begin(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GesturePinchBeginEvent,
|
||||
) {
|
||||
self.inner().gesture_pinch_begin(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_pinch_update(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GesturePinchUpdateEvent,
|
||||
) {
|
||||
self.inner().gesture_pinch_update(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_pinch_end(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GesturePinchEndEvent,
|
||||
) {
|
||||
self.inner().gesture_pinch_end(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_hold_begin(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GestureHoldBeginEvent,
|
||||
) {
|
||||
self.inner().gesture_hold_begin(seat, data, event);
|
||||
}
|
||||
|
||||
fn gesture_hold_end(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &pointer::GestureHoldEndEvent,
|
||||
) {
|
||||
self.inner().gesture_hold_end(seat, data, event);
|
||||
}
|
||||
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
self.inner().leave(seat, data, serial, time);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user